Sylar
Sylar

Reputation: 12092

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?

Upvotes: 1

Views: 170

Answers (2)

Prebiusta
Prebiusta

Reputation: 479

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);
};

Upvotes: 1

Rajdeep D
Rajdeep D

Reputation: 3920

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);

Upvotes: 1

Related Questions