Travis
Travis

Reputation: 1816

Inside an array of objects, how do you group object values if they share another key together?

I really apologize for the question title, I don't know how to phrase the question. I have an array of objects that have inter relationships with each other and I need to group them by those relationships with the names associated with those relationships. Here is the array of objects.

let someResponse = [
  {code: "00000000001524", relationship: "ally", name: "Batman"},
  {code: "00000000001524", relationship: "other", name: "Superman"},
  {code: "00000000001111", relationship: "adversary", name: "Scarecrow"},
  {code: "00000000008888", relationship: "neutral", name: "Strange"},
  {code: "00000000008888", relationship: "ally", name: "Robin"},
  {code: "00000000008888", relationship: "neutral", name: "Vale"}
];

Notice how you have similar codes and with those similar codes you will notice relationships and with those relationship, names. I need the names grouped based on the realtionship while ensuring it is in the same code. The result will look like this...

result = [
    {code: "00000000001524", relationship: "ally", names: ["Batman"]},
    {code: "00000000001524", relationship: "other", names: ["Superman"]},
    {code: "00000000001111", relationship: "adversary", names: ["Scarecrow"]},
    {code: "00000000008888", relationship: "neutral", names: ["Strange", "Vale"]},
    {code: "00000000008888", relationship: "ally", names: ["Robin"]}
]

Of course I could do a compare function and sort by code and then by relationship, but that will end changing my array of objects and I need the same code order otherwise it will mess things up later in my code, so I don't want to do that. I'm really stumped on how to do this.

I'm guessing you would start off by doing something like this...?

function rearrangeObject(response){
  let newArrangedArr = [];
  for(let i = 0; i<response.length;){
    let count = i;
    while(response[i].code === response[count].code){
      //Something goes here
      count++;
      if(count === response.length){break;}
    } 
    i = count;
  }
  return newArrangedArr;
}

Upvotes: 0

Views: 149

Answers (1)

Nick Parsons
Nick Parsons

Reputation: 50759

You can use .reduce() with an object as an accumulator. For each key in the object, you can use a combination of the code and relationship. Then, for each object in your array, if you encounter a key in the accumulator object which matches the code+relationship of your current object you can add the name to value array held by the accumulator:

const someResponse = [
  {code: "00000000001524", relationship: "ally", name: "Batman"},
  {code: "00000000001524", relationship: "other", name: "Superman"},
  {code: "00000000001111", relationship: "adversary", name: "Scarecrow"},
  {code: "00000000008888", relationship: "neutral", name: "Strange"},
  {code: "00000000008888", relationship: "ally", name: "Robin"},
  {code: "00000000008888", relationship: "neutral", name: "Vale"}
];

const res = Object.values(someResponse.reduce((acc, {code, relationship, name}) => {
  const key = `${code}-${relationship}`;
  acc[key] = acc[key] || {code, relationship, names: []}
  acc[key].names.push(name);
  return acc;
}, {}));

console.log(res);

If you don't feel comfortable using .reduce() and destructuring, you can achieve the same result using a for loop with an object, which, in essence, does the same thing:

const someResponse = [
  {code: "00000000001524", relationship: "ally", name: "Batman"},
  {code: "00000000001524", relationship: "other", name: "Superman"},
  {code: "00000000001111", relationship: "adversary", name: "Scarecrow"},
  {code: "00000000008888", relationship: "neutral", name: "Strange"},
  {code: "00000000008888", relationship: "ally", name: "Robin"},
  {code: "00000000008888", relationship: "neutral", name: "Vale"}
];

const acc = {};
for(const obj of someResponse) {
  const key = obj.code + '-' +obj.relationship;
  acc[key] = acc[key] || {code: obj.code, relationship: obj.relationship, names: []} // use object sotred at key or default it to a new object with a `names` array
  acc[key].names.push(obj.name); // push the current name into the object;
}
const res = Object.values(acc);
console.log(res);

Upvotes: 2

Related Questions