3

I have the object arrays like below:

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

And what I want is to group by id, name and list out rating belonging its like below

const users = [
  {
    id: 1,
    name: "Spider-man",
    rating: {top: 1, middle: 4.5, bottom: 5}
  },
  {
    id: 2,
    name: "Iron man",
    rating: {top: 3, middle: 3.5, bottom: 2}
  },
  {
    id: 3,
    name: "Hulk",
    rating: {top: 5, middle: 1.5, bottom: 4}
  }
];

I've tried this approach but seems it can achieve in more ways such as .reduce, for...of with more elegant, right?

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

var result = obj.top.map(x => ({
  id: x.id,
  name: x.name,
  rating: {
    top: obj.top.find(t => t.id === x.id).rating,
    middle: obj.middle.find(t => t.id === x.id).rating,
    bottom: obj.bottom.find(t => t.id === x.id).rating,
  }
}));
                          
console.log(result);

Any other ways to achieve it? Thanks in advance.

5 Answers 5

3

You need to map one of the subarrays to find each character's rating, so I think your current approach is pretty reasonable. You can make it a bit less repetitive by making an array of the properties (top, middle, bot) beforehand, then iterating over them instead of listing each different one:

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

const props = ['top', 'middle', 'bottom'];
var result = obj.top.map(x => ({
  id: x.id,
  name: x.name,
  rating: Object.fromEntries(
    props.map(prop =>
      [prop, obj[prop].find(t => t.id === x.id).rating]
    )
  )
}));      
console.log(result);

Another approach that's less computationally complex:

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};
const byName = {};
for (const [prop, arr] of Object.entries(obj)) {
  for (const item of arr) {
    byName[item.name] ??= { ...item, rating: {} };
    byName[item.name].rating[prop] = item.rating;
  }
}
 
console.log(Object.values(byName));

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

6 Comments

Yeah, Very like with your approach You can make it a bit less repetitive, +1
The second solution looks the same to me, but your code is more elegant and concise, sir. Thanks for the useful.
@Phong developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Shortcut for if (!byName[item.name]) byName[item.name] = { ...item, rating: {} }
One more question: the second solution would be better than the first by using a dictionary with time complexity when searching is O(1) . But as you can see, the output just contains 3 items. So the trade-off is here space & time complexity, right?
@Phong First code is worse in terms of both time and space complexity, since it has to .find and also has to construct bunches of intermediate arrays. But whether complexity is actually an issue to worry about depends on the sorts of inputs you're really dealing with. if it's only 3 items, it doesn't matter, you can pick whatever "feels" nicer
|
1

You could do it in a one-liner way

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 },
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 },
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 },
  ],
};

const res = Array.from(
  Object.entries(obj)
    .flatMap(([rater, ratee]) =>
      ratee.map(({ id, name, rating }) => ({
        id,
        name,
        [rater]: rating,
      }))
    )
    .reduce(
      (acc, { id, ...restInfo }) =>
        acc.set(id, { ...(acc.get(id) || {}), ...restInfo }),
      new Map()
    )
).map(([id, { name, ...rating }]) => ({ id, name, rating }));

console.log(res);

1 Comment

Yeah, another nice approach by using .flatMap and reduce. Just a little bit about complexity concerns. Anyway, thank you +1
1

Using Dictionary along with Logical nullish assignment (??=)

The main idea includes 2 steps:

  1. Loop all [key, values] of the object.
  2. Inner each the values of the object, we loop to determine which the user's rating along with key belongs by user.id.

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

// Refactor code: using Dictionary along with `Logical nullish assignment (??=)` .
var result = Object.entries(obj).reduce((acc, [key, values]) => {
  values.forEach(v => {
    acc[v.id] ??= {...v, rating: {}};
    acc[v.id].rating[key] = v.rating;
  });
  
  return acc;
}, {});
console.log(Object.values(result));

/* Old versions: using Array approach

 var result = Object.entries(obj).reduce((acc, [key, values]) => {
  values.forEach(v => {
    var x = acc.find(r => r.id === v.id);
    if(x !== undefined){
      x.rating[key] = v.rating;
    }else{
        x = { id: v.id, name: v.name, rating: {[key]: v.rating} };
        acc.push(x);
      }
  });
  
  return acc;
}, []);
*/

More detailed explanation:

The logical nullish assignment (x ??= y) operator only assigns if x is nullish (null or undefined).

Comments

0

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};


const tempArr1 = Object.keys(obj).reduce((arr, key) => {
  obj[key].forEach((item) => {
    arr = [...arr, { ...item, rating: { [key]: item.rating } }];
  });
  return arr;
}, []);

const result = tempArr1.reduce((arr, el) => {
  let tempObj = { ...el };
  const index = arr.findIndex((tempItem) => tempItem.id === tempObj.id);
  if (~index) {
    arr[index] = {
      ...tempObj,
      rating: {
        ...arr[index].rating,
        ...tempObj.rating
      }
    };
  } else {
    arr = [...arr, tempObj];
  }
  return arr;
}, []);

console.log(result);

Comments

0

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

var result = [];
for(let [key, values] of Object.entries(obj))
  for(let item of values){
    let x = result.find(r => r.id === item.id);
    if(x !== undefined){
      x.rating[key] = item.rating;
    }else{
        x = { id: item.id, name: item.name, rating: {[key]: item.rating} };
        result.push(x);
      }
  }
console.log(result);

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.