I've got an array of objects that looks like this:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 13 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }]
and where there are multiple objects for a given person -> I want to keep the top "X". For example:
a. top 1 result for a person Result would look like this:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Joe', scoreTotal: 28 }]
b. top 2 results for a person Result would look like this:
[ { person: 'Fred', scoreTotal: 29 },
{ person: 'Alice', scoreTotal: 34 },
{ person: 'Alice', scoreTotal: 22 },
{ person: 'Mary', scoreTotal: 14 },
{ person: 'Bob', scoreTotal: 33 },
{ person: 'Bob', scoreTotal: 22 },
{ person: 'Joe', scoreTotal: 28 }]
Is there a way to achieve this using something like Lodash?
I think I'm heading in the right direction with this but not quite there yet:
for (var i = 0; i < t.length - 1; i++) {
if (
t[i].golfer === t[i + 1].golfer &&
t[i].scoreTotal < t[i + 1].scoreTotal
) {
delete t[i];
}
}
// remove the "undefined entries"
t = t.filter(function(el) {
return typeof el !== "undefined";
});
console.log(t);
You can do this pretty simply without Underscore/Lodash. Sort the array highest to lowest by score. Then use Array.filter. As filter goes through the array keep track of how many times you see each person and start return false after the top number you want has been reached for a person.
let arr = [ { person: 'Fred', scoreTotal: 29 },{ person: 'Alice', scoreTotal: 34 },{ person: 'Alice', scoreTotal: 22 },{ person: 'Mary', scoreTotal: 14 },{ person: 'Bob', scoreTotal: 33 },{ person: 'Bob', scoreTotal: 13 },{ person: 'Bob', scoreTotal: 22 },{ person: 'Joe', scoreTotal: 28 }]
function filterTop(arr, top) {
let counts = {}
return [...arr].sort((a, b) => b.scoreTotal - a.scoreTotal)
.filter(score => (counts[score.person] = (counts[score.person] || 0) +1 ) <= top)
}
console.log(filterTop(arr, 1))
console.log(filterTop(arr, 2))
Using only ES6, you can use 2 reduce
s. First is to group the arrays. the second is to get the number of top per group.
let arr = [{"person":"Fred","scoreTotal":29},{"person":"Alice","scoreTotal":34},{"person":"Alice","scoreTotal":22},{"person":"Mary","scoreTotal":14},{"person":"Bob","scoreTotal":33},{"person":"Bob","scoreTotal":13},{"person":"Bob","scoreTotal":22},{"person":"Joe","scoreTotal":28}];
let getTop = (a, t) => { //Parameters a = array. t = top
return Object.values(a.reduce((c, v) => { //Group the array using the person property
c[v.person] = c[v.person] || [];
c[v.person].push(v);
return c;
}, {})).reduce((c, v) => {
v.sort((a, b) => b.scoreTotal - a.scoreTotal); //Sort the sub array
c = c.concat(v.slice(0, t)); //Add the top to accumulator
return c;
}, []);
}
let result1 = getTop(arr, 1); //Get top 1
let result2 = getTop(arr, 2); //Get top 2
console.log('Top 1', result1);
console.log('Top 2', result2);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With