Pi L
Pi L

Reputation: 59

Filter array of objects by comparing their properties

I tried to find the highest score on an object in this array, but the result is incorrect. Should we reset the score to be 0 again? I was trying to put the score variable below the obj[i.class] but nothing changed:

function theScore (students) {
  var obj = {};
  score = 0;
  for(i of students){
    if(score < i.score) {
      obj[i.class] = {
        name: i.name,
        score: i.score
      };
    };
  };
  return obj;
};


console.log(theScore([
  {
    name: 'Sara',
    score: 90,
    class: 'A'
  },
  {
    name: 'Poyi',
    score: 85,
    class: 'B'
  },
  {
    name: 'Adert',
    score: 74,
    class: 'A'
  },
  {
    name: 'Shynta',
    score: 78,
    class: 'B'
  }
]));

Desired output:

{
A: { 
        name: 'Sara',
        score: 90
   },
B: {
        name: 'Poyi',
        score: 85
   }
}

Upvotes: 1

Views: 72

Answers (5)

Akrion
Akrion

Reputation: 18515

The issue with your code was never actually comparing the the actual object score but to 0 always.

If you really want to use for in loop for this you can do something like (with slight changes to your code):

var data = [ { name: 'Sara', score: 90, class: 'A' }, { name: 'Poyi', score: 85, class: 'B' }, { name: 'Adert', score: 74, class: 'A' }, { name: 'Shynta', score: 78, class: 'B' } ]

function theScore (students) {
  var obj = {}
  for(i of students){
    if(!obj[i.class] || obj[i.class].score < i.score) {
      obj[i.class] = {name: i.name, score: i.score}
    }
  }
  return obj
}

console.log(theScore(data))

Or you can use reduce and solve it like this:

var data = [ { name: 'Sara', score: 90, class: 'A' }, { name: 'Poyi', score: 85, class: 'B' }, { name: 'Adert', score: 74, class: 'A' }, { name: 'Shynta', score: 78, class: 'B' } ]

const maxByClass = (d) => d.reduce((r,{name, score, ...c}) => {
  r[c.class] = r[c.class] ? r[c.class].score > score ? r[c.class] : {name, score} : {name, score}
  return r
}, {})

console.log(maxByClass(data))

Since reduce returns an accumulator we simply need to assign the correct object prop based on which one of the 2 classes scores is larger and return.

Upvotes: 0

Matt Morgan
Matt Morgan

Reputation: 5303

The problem with your code is that you don't make any distinction between students' scores in one class versus another class. You also are not updating the score when you find a higher one, so every student's score (unless it's 0) will be higher than score every time your loop runs, and you just end up with the last score in the list.

EDIT: edited to show how to do this with only one loop

You need to:

  1. Track score for each class
  2. Loop over the students
  3. Compare that student's score to the class high score for that student's class
  4. Update as needed
  5. Repeat until done.

function theScore (students) {
  const highScoresByClass = {};
  const scores = {A: 0, B: 0, C: 0};
  for (const student of students){
    const classHighScore = scores[student.class];
    if(classHighScore < student.score) {
      scores[student.class] = student.score;
      highScoresByClass[student.class] = {
        name: student.name,
        score: student.score
      };
    };
  };
  return highScoresByClass;
};


console.log(theScore([
  {
    name: 'Sara',
    score: 90,
    class: 'A'
  },
  {
    name: 'Poyi',
    score: 85,
    class: 'B'
  },
  {
    name: 'Adert',
    score: 74,
    class: 'A'
  },
  {
    name: 'Shynta',
    score: 78,
    class: 'B'
  }
]));

Upvotes: 0

Ele
Ele

Reputation: 33726

You need to track the already checked classes.

A cleaner alternative is using the function Array.prototype.reduce to build the desired output. As you can see, basically, this approach is keeping a track of the previously checked classes.

function theScore (students) {
  return students.reduce((a, c) => { 
    a[c.class] = (a[c.class] || (a[c.class] = c));
    if (c.score > a[c.class].score) a[c.class] = c;
    return a;
  }, Object.create(null));
}


console.log(theScore([  {    name: 'Sara',    score: 90,    class: 'A'  },  {    name: 'Poyi',    score: 85,    class: 'B'  },  {    name: 'Adert',    score: 74,    class: 'A'  },  {    name: 'Shynta',    score: 78,    class: 'B'  }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Edwin Dijas Chiwona
Edwin Dijas Chiwona

Reputation: 620

The score is not being changed after you find the highest score. Change the score to the current score. Thanks. Instead of an object with map, use an object with index(Array). This should work.

function theScore (students) {
  var obj = [];
  score = 0;
  for(i of students){
    if(obj.length === 0) {
       obj.push( i );
    } else {
      for( var x = 0; x < obj.length; x++ ){
         if( i.score > obj[x].score ){
             obj.splice( x, 0, i );
             break;
         }
      }
      if( x === obj.length ){
        obj.push(i);
      }
    }
  };
  return obj;
};


console.log(theScore([
  {
    name: 'Sara',
    score: 90,
    class: 'A'
  },
  {
    name: 'Poyi',
    score: 85,
    class: 'B'
  },
  {
    name: 'Adert',
    score: 74,
    class: 'A'
  },
  {
    name: 'Shynta',
    score: 78,
    class: 'B'
  }
]));

Upvotes: 0

Vexen Crabtree
Vexen Crabtree

Reputation: 359

Can't you massively simplify your function's obj assignment?

function theScore (students) {
    var obj = {};
    var score = 0;
    for(i of students){
        if(i.score > score ) { 
            score = i.score;     // Keep track of the highest-score-found-so-far
            obj = i;             // Keep track of the highest scoring object
        }
    }
    return obj;
}

Upvotes: 1

Related Questions