0

I faced a challenge where I needed to summarize an array of objects by the object's keys. I found a solution, but I can't shake off the feeling, that my approach is pretty naive:

const objArr = [
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
];

let tempArr = [];
let uniqueIdArr = [];
let sortedArr = [];

objArr.forEach((obj) => {
    tempArr.push(obj.id);
    uniqueIdArr = [...new Set(tempArr)];
});

uniqueIdArr.forEach((uniqueId) => {
    let arr = [];
    objArr.forEach((obj) => {
        if (obj.id == uniqueId) {
            arr.push(obj.val);
        }
    });
    sortedArr.push({
        id: uniqueId,
        vals: arr,
    });
});

console.log(sortedArr);
// Output: [{ id: 1, vals: [ '', '', '' ] }, { id: 2, vals: [ '', '', '' ] }]

Maybe there is something I don't know about JavaScript's array methods yet? Is this approach totally wrong? Is there another way, so that I could reduce the code and make it more elegant?

So many questions...

Any hint or explanation would be much appreciated.

Thanks in advance

J.

  • speaking about reduce the code you can use array.reduce – cmgchess Jul 01 '22 at 11:30
  • something similar i had by calling Object.values on reduced result https://stackoverflow.com/questions/31688459/group-array-items-using-object/71637674#13583510 – cmgchess Jul 01 '22 at 11:38

3 Answers3

1

you can use Array.prototype.reduce to make your code bit shorter:

const objArr = [
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
];

let result = objArr.reduce((acc,e) => {
    let idx = acc.findIndex(s => s.id === e.id)
    if(idx > -1){
        acc[idx].vals.push(e.val)
    }
    else{
        acc.push({id:e.id,vals:[e.val]})
    }
    return acc
},[])


console.log(result)
Alan Omar
  • 4,023
  • 1
  • 9
  • 20
1

Your ideas are good and explicit, but far from being optimal.

const objArr = [
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
];
const idValMap = new Map();

objArr.forEach(o=>{
   let vals = idValMap.get(o.id);
   if(!vals){
       vals = [];
       idValMap.set(o.id,vals);
   }
   vals.push(o.val);
});

console.log(Array.from(idValMap.entries()));

You can do most of it in just one loop. Take the key, check if you saw it already, if not initialize. That's it

Salketer
  • 14,263
  • 2
  • 30
  • 58
  • `Map.prototype.entries()` returns _iterator_ not an array. Furthermore, source objects do not contain `key` property. if `vals` is supposed to be an array, `!vals` will never be faulsy. also items of `vals` should be objects with `id` property. – Yevhen Horbunkov Jul 01 '22 at 11:40
  • Thanks for pointing out my mistakes. As for vals, it will not be an array when not initialized. – Salketer Jul 01 '22 at 11:43
  • Thank you for taking the time to answer the question. After @user2371266' answer, your answer is the most understandable for me. – Jérôme Bucquet Jul 01 '22 at 15:13
0

This solution probably isn't much better but it is does use less code:

const objArr = [
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 1, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
    { id: 2, val: "" },
];

let sortedArr = [];

objArr.forEach((item) => {
    const exists = sortedArr.filter(i => i.id === item.id).length; // Check to see if we've already added an item with this ID
    if (!exists) {
        const matches = objArr.filter(i => i.id == item.id); // get all items with this ID

        sortedArr.push({
            id: item.id,
            vals: matches.map(m => m.val) // We only care about the val property
        });
    }
});

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter for informationa bout .map() and .filter() respectively

user2371266
  • 163
  • 8
  • Thank you so much for your answer. Actually, yours is the only one I can really understand as someone who started coding less than half a year ago. And you should only use code you understand, isn't it? I'm having a hard time deciding which answer to mark as correct, but yours was the most helpful to me. – Jérôme Bucquet Jul 01 '22 at 15:00