0

How can I create arrays to group members depending on their tag?

Tags can be anything, these are just examples.

Example Input

[
  { tag: 'Red', member: 'Red-Steve' },
  { tag: 'Red', member: 'Red-Adam' },
  { tag: 'Blue', member: 'Blue-John' },
  { tag: 'Blue', member: 'Blue-James' },
  { tag: 'Blue', member: 'Blue-Igor' }
]

Example Output

{
  Red: ['Red-Steve', 'Red-Adam']
  Blue: ['Blue-John', 'Blue-James', 'Blue-Igor']
}

Edit: I just made a test case on jsperf to compare the efficiency of the different answers and also the answer from the suggested duplicate question https://jsperf.com/group-objects-reduce-vs-for-loop

The answer by @Majed Badawi was the most efficient

Syntle
  • 5,168
  • 3
  • 13
  • 34
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – user120242 May 29 '20 at 02:27
  • I edited my question with the jsperf link that shows the most efficient way of doing this. – Syntle May 29 '20 at 03:47

4 Answers4

1

You can use a simple set to group them by tag as follows:

let list = [
            { tag: 'Red', member: 'Red-Steve' },
            { tag: 'Red', member: 'Red-Adam' },
            { tag: 'Blue', member: 'Blue-John' },
            { tag: 'Blue', member: 'Blue-James' },
            { tag: 'Blue', member: 'Blue-Igor' }
];
let set = {};
for(let i = 0; i < list.length; i++){
     let current = list[i];
     if(!set[current.tag])
          set[current.tag] = [current.member];
     else
          set[current.tag].push(current.member);
}
console.log(set);
Majed Badawi
  • 27,616
  • 4
  • 25
  • 48
1

collect into object map using tag as key and appending to an array

d=[
  { tag: 'Red', member: 'Red-Steve' },
  { tag: 'Red', member: 'Red-Adam' },
  { tag: 'Blue', member: 'Blue-John' },
  { tag: 'Blue', member: 'Blue-James' },
  { tag: 'Blue', member: 'Blue-Igor' }
]
console.log(
d.reduce((acc,{tag,member})=>({...acc, [tag]: [...acc[tag]||[], member]}),{})
)
user120242
  • 14,918
  • 3
  • 38
  • 52
  • sure, it is iterating over the array of objects using reduce, using destructuring to extract `tag` and `member` property, acc is the "accumulated" value returned after every iteration, then it is merging the accumulated object map with a new property [tag] (which will be Red or Blue) and then concatenating member to the array – user120242 May 28 '20 at 23:41
  • all of these answers are basically doing the same thing. mine may incur a performance penalty on very large operations due to creating an object on every iteration. it just happens to take a shorter "functional" approach – user120242 May 28 '20 at 23:47
  • That's what I'm trying to decide here since I might end up working with a large number of objects, do you think a `for` loop would be more efficient than `reduce` here? – Syntle May 28 '20 at 23:49
  • I'd go with whatever you're comfortable with. I don't think you'll actually see a noticeable difference if your data structure is like the one you gave. And, if you used `reduce` without using the `spread` syntax to merge objects (for example you could use Object.assign instead or direct assignment like the other answers), you could avoid the object initialization penalty. If you want to be sure, I'd do a jsperf benchmark with something as close as possible to your dataset to check. – user120242 May 29 '20 at 00:00
  • I just benchmarked with 100 objects and the `for` loop was obviously the winner. I used https://jsben.ch/ for the record. – Syntle May 29 '20 at 00:31
  • Ah, that's good. You should post it as a comment on your question, for future users to see. – user120242 May 29 '20 at 00:33
  • Sorry, I should post what exactly? – Syntle May 29 '20 at 00:33
  • The link to your jsperf benchmark. You save the url to your bench as a comment on this question. It'll probably be helpful to future search results for people who wanted to know the performance impact. – user120242 May 29 '20 at 00:34
1

The following code will do what you ask. It uses the object[variableKey] syntax to get what you are looking for.

const members=[
  { tag: 'Red', member: 'Red-Steve' },
  { tag: 'Red', member: 'Red-Adam' },
  { tag: 'Blue', member: 'Blue-John' },
  { tag: 'Blue', member: 'Blue-James' },
  { tag: 'Blue', member: 'Blue-Igor' }
]

//this will be the output object
const newObj={};

for(memberObj of members){
    const newKey = memberObj.tag;

    //if the key already existed, just add the new item to the existing array
    if(newObj[newKey]){newObj[newKey].push(memberObj.member);}
    //if the key didn't exist, create an array value for it with the new object
    else{newObj[newKey]=[memberObj.member];}
}

console.log(newObj);
Mark Colby
  • 191
  • 6
0

I think this will be a good way of achieving it in modern syntax. But may be there are some aggregate functions too.

var arr = [
  { tag: 'Red', member: 'Red-Steve' },
  { tag: 'Red', member: 'Red-Adam' },
  { tag: 'Blue', member: 'Blue-John' },
  { tag: 'Blue', member: 'Blue-James' },
  { tag: 'Blue', member: 'Blue-Igor' }
];

var newArr = [];
arr.forEach(o => {
  var existing = newArr[o.tag] && newArr[o.tag].length > 0 ? newArr[o.tag] : [];
  newArr[o.tag] = [...existing, ...[o.member]];
});

console.log('newArr', newArr);

In Chrome it is giving following output in the console

Chrome Console Output

We have a similar question here Most efficient method to groupby on an array of objects

muasif80
  • 5,586
  • 4
  • 32
  • 45
  • If you see a dupe you should vote to close and reference that as a duplicate. – user120242 May 29 '20 at 02:28
  • Hmm. Good suggestion – muasif80 May 29 '20 at 06:57
  • As I mentioned in the latest edit of my question, I added a link to a test case that tests the accepted answer of the question you linked as well as the 2 methods answered here. The answer of the question you linked was 20% slower than the accepted answer of this question. – Syntle May 29 '20 at 15:49