Festina
Festina

Reputation: 325

How to deep filter in array with objects in JS

I have an array and want to get just object { id: 4, name: 'name4' },

const example = [
    {
      id: '1234',
      desc: 'sample1',
      items: [
        { id: 1, name: 'name1' },
        { id: 2, name: 'testItem2' }
       ]
      },    
    {
        id: '3456',
        desc: 'sample2',
      items: [
         { id: 4, name: 'name4' },
         { id: 5, name: 'testItem5' }
       ]
    },

I try in this way.

const name = 'name4';
example.forEach((item) => item.items.find((i) => i.name === name));

But get undefined.

Upvotes: 1

Views: 90

Answers (4)

Cat
Cat

Reputation: 4246

You could use a recursive search function. Here's a detailed example:

// Applies the function recursively from the top of the data tree ("depth-first")
const
  data = getData(),
  id = 4,
  result = findById(data, id);
console.log(result ?? `No item with id ${id} found`);

// Defines the function
function findById(haystack, needleId){
  let needle = null; // Defaults to null if no match at or below this level

  // Iterates through all the items at this level
  for(const item of haystack){
    if(item.id == needleId){
      // Qapla': Quits early, passes honorable item up to previous level
      needle = item;
      return needle;
    }
    else {
      // Checks children, grandchildren, etc, before continuing iteration
      const nextHaystack = item.items;
      if(nextHaystack?.length){
        needle = findById(nextHaystack, needleId); // Recursive call
      }
    }
    // Done searching children, continues to next iteration at this level
  }
  // Done searching this level, returns result up to previous level
  return needle;
}

// Gets the initial data
function getData(){
  return [
    {
      id: '1234',
      desc: 'sample1',
      items: [ { id: 1, name: 'name1' }, { id: 2, name: 'testItem2' } ]
    },
    {
      id: '3456',
      desc: 'sample2',
      items: [ { id: 4, name: 'name4' }, { id: 5, name: 'testItem5' } ]
    }
  ];
}

Upvotes: 1

xlm
xlm

Reputation: 7594

forEach doesn't do what you think it does. From the docs:

The forEach() method executes a provided function once for each array element.

...

Return value
undefined.

So if you want to use forEach you need to save the value:

const example = 
  [ { id   : '1234'
    , desc : 'sample1'
    , items: 
      [ { id: 1, name: 'name1'     } 
      , { id: 2, name: 'testItem2' } 
    ] } 
  , { id   : '3456'
    , desc : 'sample2'
    , items: 
      [ { id: 4, name: 'name4'     } 
      , { id: 5, name: 'testItem5' } 
  ] } ] 
  
const results = []; // Store matches here

const name = 'name4';
example.forEach((item) => {
  const res = item.items.find((i) => i.name === name);
  if (res !== undefined) {
    results.push(res);
  }
});

console.log(results);

IMHO I would suggest a more functional approach using flatMap and filter instead of forEach.

Lastly, note that in my above snippet, I'm storing the results in an array as it's not entirely clear to me that you won't have multiple matches per your example. But if you're sure that you will only ever have one result then a simple for loop works better, especially if you have a large array of items:

let result = null;
for (let i = 0; i < example.length; i++) {
  const res = example[i].items.find((j) => j.name === name);
  if (res !== undefined) {
    result = res;
    break; // No need to iterate further
  }
}
console.log(result);

Upvotes: 2

Mister Jojo
Mister Jojo

Reputation: 22355

this way...?

const example = 
  [ { id   : '1234'
    , desc : 'sample1'
    , items: 
      [ { id: 1, name: 'name1'     } 
      , { id: 2, name: 'testItem2' } 
    ] } 
  , { id   : '3456'
    , desc : 'sample2'
    , items: 
      [ { id: 4, name: 'name4'     } 
      , { id: 5, name: 'testItem5' } 
  ] } ]; 

const rechName = (s,arr) => 
  arr.find( x =>         // find the first parent object
    x.items.some( y =>  // containing the search
     y.name === s )
   )?.items                       // if one
     .find( z => z.name === s ); // find it in!

console.log( rechName('name4', example) ) // -> { id: 4, name: 'name4' }
console.log( rechName('abc', example) )  // -> undefined

Upvotes: 2

flyingfox
flyingfox

Reputation: 13506

You can using flatMap() to do it

const example = [
    {
      id: '1234',
      desc: 'sample1',
      items: [
        { id: 1, name: 'name1' },
        { id: 2, name: 'testItem2' }
       ]
      },    
    {
        id: '3456',
        desc: 'sample2',
        items: [
         { id: 4, name: 'name4' },
         { id: 5, name: 'testItem5' }
       ]
    }]
    
const name = 'name4';
let result = example.flatMap(e => e.items).filter(d => d.name == name)
console.log(result)

Upvotes: 3

Related Questions