简体   繁体   中英

Retain array structure when filtering nested array

My brain froze with this advanced filtering. This task has exceeded my basic knowledge of filter , map etc.

Here I have an array with nested objects with array:

const DATA = [
    {
        title: 'Spongebob',
        data: [
            { id: 1, name: 'Mr Crabs' },
            { id: 2, name: 'Sandy' }
        ]
    },
    {
        title: 'Dragon Balls Z',
        data: [
            { id: 1, name: 'GoKu' },
            { id: 2, name: 'Zamasu' }
        ]
    }
];

You may have seen this sort of style if you've worked with React Native (RN). This question is not for RN. I need to perform a filter on the name property in the nested array and when I get a match, I must return the format as the DATA variable.

const handleFiltering = (value) => {

    const _value = value.toLowerCase();

    const results = DATA.map(o => {
        return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
    });

    console.log(results);
};

My limited knowledge of deep filtering returns the basic filtering for the data array but need to retain the structure for DATA . The expected results I'd expect:


// I'm now querying for "ZAMASU"

const handleFiltering = (value='ZAMA') => {

    const _value = value.toLowerCase();

    const results = DATA.map(o => {
        return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
    });

    // console.log(results) should now be
    // [
    //  {
    //      title: 'Dragon Balls Z',
    //     data: [
    //          { id: 2, name: 'Zamasu' }
    //      ]
    //  }
    // ];
};

What comes to mind is the use of {...DATA, something-here } but my brain has frozen as I need to get back the title property. How to achieve this, please?

You can use reduce method of array. First find out the object inside data array and then add that to accumulator array as new entry by preserving the original structure.

 const DATA = [ { title: 'Spongebob', data: [ { id: 1, name: 'Mr Crabs', where: 'tv' }, { id: 2, name: 'Sandy' } ] }, { title: 'Dragon Balls Z', data: [ { id: 1, name: 'GoKu' }, { id: 2, name: 'Zamasu' } ] } ]; let handleFiltering = (value='tv') => { return DATA.reduce((acc,d) => { let obj = d.data.find(a => a.name?.toLowerCase().includes(value.toLowerCase()) || a.where?.toLowerCase().includes(value.toLowerCase())); obj? acc.push({...d, data:[obj]}): null; return acc; }, []); } let result = handleFiltering(); console.log(result);

Another solution would be first use filter to find only objects containing the name in data passed through the argument, subsequently mapping data.

Here is your adjusted filter method

const handleFiltering = (value) => {
  const _value = value.toLowerCase();

  const results = DATA.filter((obj) =>
    obj.data.some((character) => character.name.toLowerCase() === _value)
  ).map((obj) => ({
    title: obj.title,
    data: obj.data.filter(
      (character) => character.name.toLowerCase() === _value
    ),
  }));

  console.log(results);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM