0

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.

0

6 Answers 6

2

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; 
},{})
Sign up to request clarification or add additional context in comments.

Comments

1

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)

Comments

1

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);

Comments

1
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}))

Comments

1

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);

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.