Search code examples
javascriptreactjsarraysjsonloops

Filter array of objects and get properties into a new array by iterating through the array of objects


I have input as follows

[
    {
        "metadata": { "id": 1071, "name": "First" },
        "languages": [
            { "name": "Alpha", "details": [ { "city": "usa", "count": 33 } ] },
            { "name": "Beta", "details": [ { "city": "japan", "count": 3 } ] },
        ]
    },
    {
        "metadata": { "id": 1068, "name": "Second" },
        "languages": [
            { "name": "Alpha", "details": [ { "city": "japan", "count": 10 }, { "city": "usa", "count": 20 } ] },
            { "name": "Beta", "details": [ { "city": "japan", "count": 15 }, { "city": "usa", "count": 25 } ] },
        ]
    },
]

So here is my scenario where i have to filter by language Alpha. So i have to go through first object and filter languages by Alpha and then get usa and japan count into a new array. Similarly loop through 2nd object and extract that count.

At the end, i should get below sequence of counts in the new array

output = { japan: [0, 10], usa: [33, 20] } //for filter by Alpha
output = { japan: [3, 15], usa: [0, 25] } //for filter by Beta

As you can see if the details array doesnot have japan or usa count in it, we are pushing 0 for that.

I tried the following but couldnot achieve the desired result using reduce. Am i missing anything here ?

let output = input.reduce(
      (result, { languages }) => {
        result.japan.push(languages.details.count || 0);
        result.usa.push(languages.details.count || 0);
        return result;
      },
      { japan: [], usa: [] }
    );

Solution

  • result.japan.push(languages.details.count || 0);
    

    This wont work, details is an array, so you can't call .count on it.

    You'll need to loop over the details and then add the count for each city, However, you need to catch the missing state.

    I've used an inner reduce to map the details array to an city/count object.

    This way you can change your attempt to:

    p.usa.push(groupedOnCityCount?.usa || 0);
    p.japan.push(groupedOnCityCount?.japan || 0);
    

    to catch the missing country.


    This Demo should get you started:

    const data = [
        {
            "metadata": { "id": 1071, "name": "First" },
            "languages": [
                { "name": "Alpha", "details": [ { "city": "usa", "count": 33 } ] },
                { "name": "Beta", "details": [ { "city": "japan", "count": 3 } ] },
            ]
        },
        {
            "metadata": { "id": 1068, "name": "Second" },
            "languages": [
                { "name": "Alpha", "details": [ { "city": "japan", "count": 10 }, { "city": "usa", "count": 20 } ] },
                { "name": "Beta", "details": [ { "city": "japan", "count": 15 }, { "city": "usa", "count": 25 } ] },
            ]
        },
    ];
    
    const filterBy = (data, name) => {
        return data.reduce((p, c) => {
            for (let langObj of c.languages) {
                if (langObj.name === name) {
                    const groupedOnCityCount = langObj.details.reduce((x, {city,count}) => ({ ...x, [city]: count }), {});
                    p.usa.push(groupedOnCityCount?.usa || 0);
                    p.japan.push(groupedOnCityCount?.japan || 0);
                }
            }
            return p;
        }, { japan: [], usa: [] });
    }
    
    const A = filterBy(data, 'Alpha');
    const B = filterBy(data, 'Beta');
    
    console.log(A, B);