Reputation: 33
Given I have the following array of objects:
const data = [
{
name: 'item1',
score: 5,
colour: 'red'
},
{
name: 'item2',
score: 5,
colour: 'blue'
},
{
name: 'item1',
score: 5,
colour: 'blue'
},
{
name: 'item3',
score: 15,
colour: 'blue'
},
{
name: 'item2',
score: 25,
colour: 'green'
}
]
I'm looking for the most semantic approach to looping through this and accumulating the score, given the name value is a match - example result (we don't care about the colour key):
[
{
name: item1,
score: 10
},
{
name: item2,
score: 30
},
{
name: item3,
score: 15
}
]
The final order isn't of importance, but the data will be generated randomly by site visitors (so we have no prior knowledge of each name for example) there could be 2 entries or a maximum of 15.
Right now, I am solving this with what feels like a long winded (Sunday evening!) approach:
const data = [
{
name: 'item1',
score: 5,
colour: 'red'
},
{
name: 'item2',
score: 5,
colour: 'blue'
},
{
name: 'item1',
score: 5,
colour: 'blue'
},
{
name: 'item3',
score: 15,
colour: 'blue'
},
{
name: 'item2',
score: 25,
colour: 'green'
}
];
let scoreData;
const checkScore = (data) => {
// check to see if we have existing data, if not create new array and pass in our initial data
if (!scoreData) {
scoreData = [];
scoreData.push(data);
}
// check to see if name is already present in our data, if so, accumulate the score
else if (scoreData.filter(e => e.name === data.name).length > 0) {
scoreData
.filter(e => e.name === data.name)
.map(e => e.score += data.score)
} else {
scoreData.push(data)
}
}
for (let i = 0; i < data.length; i++) {
// save data to new object to compare
let dataObj = {
name: data[i].name,
score: Number(data[i].score)
}
checkScore(dataObj)
}
console.log(scoreData)
My gut says there's a cleaner approach here and I would love to find one! Greatly appreciate any feedback. Apologies for the poor title, also!
Upvotes: 0
Views: 439
Reputation: 23654
array.reduce()
was made for this! It loops through each item in the array and - in this case- checks to see if we already have used that team name. If so, we add to that items 'score' value. If not, we set a new item in our return array.
Edit answer was updated to support IE which meant doing away with arrow functions and findIndex
const data = [{
name: 'item1',
score: 5,
colour: 'red'
},
{
name: 'item2',
score: 5,
colour: 'blue'
},
{
name: 'item1',
score: 5,
colour: 'blue'
},
{
name: 'item3',
score: 15,
colour: 'blue'
},
{
name: 'item2',
score: 25,
colour: 'green'
}
]
let scores = data.reduce(function(b, a) {
delete a.colour;
let ind=-1;
b.forEach(function(e,i) { if (e.name === a.name) ind = i});
if (ind > -1) b[ind].score += a.score;
else b.push(a);
return b
}, [])
console.log(scores)
Upvotes: 1