Reputation: 548
I have a array like this:
let data = [
{
name: 'foo',
score: 10,
},
{
name: 'bar',
score: 20
},
{
name: 'foo',
score: 15,
},
];
Now this represents a track records of scores by player. I want to get the highest score of each player. How can I achieve this? I tried different combinations of filters and I can't find a way without iterating multiple times over the array.
Upvotes: 0
Views: 1041
Reputation: 8671
What is the problem with iterating over the array to begin with? God doesn't kill a kitten each time you solve a problem in JavaScript without calling array.filter
or array.reduce
, you know :)
Here is another deliciously impure solution (sorry kitties, nothing personal) taking advantage of JavaScript's marvellous plasticity (using an object like an associative array)
var data = [
{ name: 'foo', score: 10 },
{ name: 'bar', score: 20 },
{ name: 'foo', score: 15 },
];
var high_score = {};
for (record of data) { // note it's the ES6 "of", not the ill-fated legacy "in" !!!
if ((high_score[record.name] ?? 0) < record.score) {
high_score[record.name] = record.score;
}
}
// "high_score" is just an object with a property named after each player
console.log(high_score)
>>> Object { foo: 15, bar: 20 }
// This object can be accessed like an associative array (a circa 1995
// ancestor of a dictionary, if you like), in a pretty intuitive way:
console.log(high_score["foo"])
>>> 15
console.log(high_score["bar"])
>>> 20
Upvotes: 0
Reputation: 167
You can solve this with reduce()
or you can do a classic solving pattern.
With Reduce()
const data = [{ name: 'foo', score: 10 }, { name: 'bar', score: 20 }, { name: 'foo', score: 15 } ];
const highsObj = data.reduce((acc, currentItem) => {
if (!acc[currentItem.name] || acc[currentItem.name].score < currentItem.score) {
acc[currentItem.name] = currentItem;
}
return acc;
}, {})
// As an array
const highsArray = Object.values(highsObj);
console.log(highsArray);
Classic pattern
const data = [{ name: 'foo', score: 10 }, { name: 'bar', score: 20 }, { name: 'foo', score: 15 } ];
const highScores = {};
for (const item of data) {
const { name, score } = item;
const currentValue = highScores[name];
if (!currentValue || currentValue.score < score) {
highScores[name] = item;
}
}
// As an array
const highScoresArray = Object.values(highScores);
console.log(highScoresArray);
Upvotes: 1
Reputation: 77
let data = [
{
name: "foo",
score: 10,
},
{ name: "bar",
score: 20 },
{
name: "foo",
score: 15,
},
];
const highestIndex = data.reduce(result, {name, score}) => {
result[name] = !result[name] || score > result[name]
? score
: result[name];
return result;
}, {})
if you need result in same structure as data then just redeem result to array using Object.entries mapping:
const highestScores =
Object
.entries(highestIndex)
.map(([name, score]) => ({name, score}))
Upvotes: 1
Reputation: 23664
Reduce()
is perfect for this kind of thing.
let data = [{
name: 'foo',
score: 10,
},
{
name: 'bar',
score: 20
},
{
name: 'foo',
score: 15,
},
];
let hs = data.reduce((b, a) => {
let i = b.findIndex(e => e.name === a.name)
if (i > -1) b[i].score = Math.max(b[i].score, a.score)
else b.push(a)
return b;
}, []);
console.log(hs)
Upvotes: 1
Reputation: 8718
I'd suggest something like this:
const data = [
{
name: 'foo',
score: 10,
},
{
name: 'bar',
score: 20
},
{
name: 'foo',
score: 15,
},
];
// We'll keep track of the max scores here
const maxScore = {};
// Iterating with destructuring to directly access name/score
for (const { name, score } of data) {
const currentScore = maxScore[name];
if (!currentScore) {
// No score saved yet, save this one
maxScore[name] = score;
} else if (currentScore < score) {
// This score is higher than the saved one
maxScore[name] = score;
} else {
// Score is lower (or equal) so do nothing
}
}
console.log(maxScore);
// If you want to convert it back to an array:
const list = Object.entries(maxScore).map(
// Object.entries returns [key, value][]
// so we map it, extract the key/value (name/score) from each
// pair and create an object from it which we return
([name, score]) => ({ name, score }));
console.log(list);
Upvotes: 1
Reputation: 3260
You can use reduce
for this.
const highScores = data.reduce((memo, entry) => {
if (!memo[entry.name] || memo[entry.name] < entry.score) {
memo[entry.name]=entry.score
}
return memo;
},{})
Upvotes: 2