0

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!

1 Answer 1

1

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)

Sign up to request clarification or add additional context in comments.

2 Comments

Wonderful, thank you @kinglish I like this a lot - if we wish to support IE, do we have a workaround for findIndex()?
@jasonewins - I updated the answer to be IE compatible.

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.