Home > Enterprise >  Functional way to find item in array from a previous index
Functional way to find item in array from a previous index

Time:07-10

I have an array of objects

Something like

const items: item[] = [
    {id: 1, type: 'parent'},
    {id: 2, type: 'parent'},
    {id: 3, type: 'child'},
    {id: 4, type: 'child'},
]

Now I want to make a function that if given an item of type child it returns the first preceding row with type parent.

I want to do this a more functional way for education purposes. I've done this imperatively as such (pseudo-code, but should work):

function getParentItem(item: item)
    let startIndex = items.indexOf(item)
    let item = null
    while(!item){
        if(items[startIndex].type == "parent"){
            item = items[startIndex]
        }
        startIndex--
    }
    return item
}
getParentItem({id: 4, type:'child'}) //Should return {id: 2, type: 'parent'}

What would be a good, immutable, functional way to do this?

CodePudding user response:

First, ensure you don't recreate the elements of your array as you did in your example. Since you're changing the reference, the indexOf function will not be able to find it. A better approach is to use the findIndex index, which receives a matcher function. It'll return the index of the first element this function resolves to true.

Then, you can use findLast, which does the same, but instead of returning for the first matching element index, it will search for the last matching element and return it instead of the index. Also, the matcher functions receive the index of the current checked element so that you can limit it.

const items = [
    {id: 1, type: 'parent'},
    {id: 2, type: 'parent'},
    {id: 3, type: 'child'},
    {id: 4, type: 'child'},
]

function getParentItem(elementId) {
  const elementIndex = items.findIndex(el => el.id === elementId);
  if (elementIndex === -1) return null;
  const parentItem = items.findLast((el, index) => index < elementIndex && el.type === 'parent');
  return parentItem || null;
}

console.log(getParentItem(3)) // { id: 2, type: parent }
console.log(getParentItem(4)) // { id: 2, type: parent }
console.log(getParentItem(5)) // null
console.log(getParentItem(2)) // { id: 1, type: parent }
console.log(getParentItem(1)) // null

Another approach may be more performant (haven't tested it), but you can write your find function, which will iterate fewer cycles than the findLast function, and in fact, it will go backward from the current element.

function findReversed(array, fromIndex, matcherFunction) {
    for (let i = fromIndex - 1; i >= 0; i--) {
        const currentItem = array[i];
        if (matcherFunction(currentItem)) {
            return currentItem; // Note how we return the value of the function here, it will not proceed to other items.
        }
    }
}

And your final result will look something like that.

const items = [
    {id: 1, type: 'parent'},
    {id: 2, type: 'parent'},
    {id: 3, type: 'child'},
    {id: 4, type: 'child'},
]

function findReversed(array, fromIndex, matcherFunction) {
    for (let i = fromIndex - 1; i >= 0; i--) {
        const currentItem = array[i];
        if (matcherFunction(currentItem)) {
            return currentItem; // Note how we return the value of the function here, it will not proceed to other items.
        }
    }
}

function getParentItem(elementId) {
  const elementIndex = items.findIndex(el => el.id === elementId);
  if (elementIndex === -1) return null;
  const parentItem = findReversed(
    items, 
    elementIndex, 
    el => el.type === 'parent'
  );
  return parentItem || null;
}

console.log(getParentItem(3)) // { id: 2, type: parent }
console.log(getParentItem(4)) // { id: 2, type: parent }
console.log(getParentItem(5)) // null
console.log(getParentItem(2)) // { id: 1, type: parent }
console.log(getParentItem(1)) // null

CodePudding user response:

function getParentItem(item){
  
    let startIndex = items.findIndex((ele,index)=>ele.id==item.id)
    let obj=null;
    while(obj==null && startIndex>=0){
      if(items[startIndex].type=="parent"){
        obj=items[startIndex]
        break;
      }else{
        startIndex--
      }
    }
    return obj
}


console.log(getParentItem({id: 4, type:'child'}))

Use this function .

  • Related