Hicki
Hicki

Reputation: 167

Return object(s) in array with highest property value per group

I have an array containing several objects similar to the following:

{person: {name: "Steve", id: 1}, role: 1}
{person: {name: "Phil", id: 2}, role: 1}
{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}

My intention is to return an array of the same type, but I'd like to return only one object per "person" with their highest role.

I understand the following will give me a single object with the highest role.

array.reduce((prev, cur) => prev.role > cur.role ? prev : cur);

How do I return each unique person and their corresponding highest role as a new array?

Like so:

{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}

Upvotes: 2

Views: 74

Answers (3)

Tom O.
Tom O.

Reputation: 5941

You could use Array.prototype.reduce and build out an object literal like below. Use the person.name property value as the object key and then just update the role value if you find a higher value:

var people = [{
  person: {
    name: "Steve",
    id: 1
  },
  role: 1
}, {
  person: {
    name: "Phil",
    id: 2
  },
  role: 1
}, {
  person: {
    name: "Steve",
    id: 1
  },
  role: 3
}, {
  person: {
    name: "Phil",
    id: 2
  },
  role: 6
}];

var highRoleByPerson = people.reduce((accum, el) => {
  if (accum[el.person.name]) {
    if (el.role > accum[el.person.name].role) {
      accum[el.person.name].role = el.role;
    }
  } else {
    accum[el.person.name] = {
      person: {
        name: el.person.name,
        id: el.person.id
      },
      role: 0
    };
  }
  return accum;
}, {});

console.log(highRoleByPerson);

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 193248

Group the items by person.id with Array.reduce(), but for each id store only the object with the highest role. Convert back to array using Object.values():

const data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }];

const result = Object.values(data.reduce((r, o) => {
  const id = o.person.id;
  
  if(!r[id] || r[id].role < o.role) r[id] = o;
  
  return r;
}, []));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386868

You need to collect the objects and if you have already one with the same id check the role and take the greater one.

var data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }],
    grouped = data.reduce((r, o) => {
        var index = r.findIndex(({ person: { id } }) => id === o.person.id);
        if (index === -1) {
            r.push(o);
            return r;
        }
        if (r[index].role < o.role) {
            r[index] = o;
        }
        return r;    
    }, []);

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 4

Related Questions