简体   繁体   中英

Group by array of objects by a nested key

I have the following data:

const data = [
    {
        parent: {
            id: "1",
            name: "Europe"
        },
        item: {
            name: "Italy"
        },
        score: 5
    },
    {
        parent: {
            id: "1",
            name: "Europe"
        },
        item: {
            name: "France"
        },
        score: 4.5
    },
    {
        parent: {
            id: "1",
            name: "Europe"
        },
        item: {
            name: "UK"
        },
        score: 4.9
    },
    {
        parent: {
            id: "2",
            name: "Afrique"
        },
        item: {
            name: "Morocco"
        },
        score: 3.1
    },
    {
        parent: {
            id: "2",
            name: "Afrique"
        },
        item: {
            name: "Egypt"
        },
        score: 3.9
    }
];

I want to group it based on the parent.id and calculate the average score, so I can have the following result:

[
    {
        parent: {
            id: "1",
            name: "Europe",
            items: [
                {
                    name: "Italy"
                },
                {
                    name: "France"
                },
                {
                    name: "UK"
                }
            ],
            score: 4.8
        }
    },
    {
        parent: {
            id: "2",
            name: "Afrique",
            items: [
                {
                    name: "Morocco"
                },
                {
                    name: "Egypt"
                }
            ],
            score: 3.5
        }
    }
]

I used the following function, but it doesn't work for the nested key, and also it's doesn't return the desired result schema.

let group = cars.reduce((r, a) => {
   console.log("a", a);
   console.log('r', r);
   r[a.make] = [...r[a.parent.id] || [], a];
   return r;
}, {});
console.log("group", group);

You can use _reduce() function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

var result = data.reduce((res, data) => {
  if(!res[data.parent.id]) {
    data.item = [data.item];
    res[data.parent.id] = data;
  } else {
    res[data.parent.id]['item'].push(data['item']);
    res[data.parent.id]['score'] = (res[data.parent.id]['score'] + data['score'])/2;
  }
  return res;
}, [])
.filter(x => x != null)

 const data = [ { parent: { id: "1", name: "Europe" }, item: { name: "Italy" }, score: 5 }, { parent: { id: "1", name: "Europe" }, item: { name: "France" }, score: 4.5 }, { parent: { id: "1", name: "Europe" }, item: { name: "UK" }, score: 4.9 }, { parent: { id: "2", name: "Afrique" }, item: { name: "Morocco" }, score: 3.1 }, { parent: { id: "2", name: "Afrique" }, item: { name: "Egypt" }, score: 3.9 } ]; var result = data.reduce((res, data) => { if(!res[data.parent.id]) { data.item = [data.item]; res[data.parent.id] = data; } else { res[data.parent.id]['item'].push(data['item']); res[data.parent.id]['score'] = (res[data.parent.id]['score'] + data['score'])/2; } return res; }, []) .filter(x => x != null) console.log(result)

Create an object/hashmap, then format the resulting object into an array.

let continents = {}
data.forEach(function(country){
    const continent_id = country.parent.id
    let continent = continents[continent_id]
    if(!continent){
        continent = {
             id: continent_id,
             name: country.parent.name,
             items: [],
        }
        continents[continent_id] = continent
    }
    continent.items.push({
       name: country.item.name,
       score: country.score
    })

})
continents = Object.entries(continents).map(item => ({parent: item[1]}))
console.log(continents)

Output:

[
    {
        "parent":{
            "id":"1",
            "name":"Europe",
            "items":[
                {
                    "name":"Italy",
                    "score":5
                },
                {
                    "name":"France",
                    "score":4.5
                },
                {
                    "name":"UK",
                    "score":4.9
                }
            ]
        }
    },
    {
        "parent":{
            "id":"2",
            "name":"Afrique",
            "items":[
                {
                    "name":"Morocco",
                    "score":3.1
                },
                {
                    "name":"Egypt",
                    "score":3.9
                }
            ]
        }
    }
]

From the data you've provided if you additionaly need to count average of score property, use the following reduce method: it will iterate trough your data, group it and calculate total score value and count of score values. And after reduce groups object perform map that will calculate average for score for all the groups using totalScore and scoreCount

 const data = [ { parent: { id: "1", name: "Europe" }, item: { name: "Italy" }, score: 5 }, { parent: { id: "1", name: "Europe" }, item: { name: "France" }, score: 4.5 }, { parent: { id: "1", name: "Europe" }, item: { name: "UK" }, score: 4.9 }, { parent: { id: "2", name: "Afrique" }, item: { name: "Morocco" }, score: 3.1 }, { parent: { id: "2", name: "Afrique" }, item: { name: "Egypt" }, score: 3.9 } ]; let group = data.reduce((acc, rec) => { if (acc.find(item => item.parent.id === rec.parent.id)) { const idx = acc.findIndex(item => item.parent.id === rec.parent.id) acc[idx].parent.items = acc[idx].parent.items.concat(rec.item) acc[idx].parent.score += rec.score acc[idx].parent.scoreCount +=1 } else { acc = acc.concat({parent: {...rec.parent, score: rec.score, items: [rec.item], scoreCount:1}}) } return acc }, []).map(it => ({parent: {id: it.parent.id, name:it.parent.name, score: (it.parent.score / it.parent.scoreCount), items: it.parent.items}})); console.log("group", group);

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