1

I have an array of objects

    listOfItems: any[] = [
      {text: 'Hola', group: 'espanian'},
      {text: 'Hello', group: 'english'},
      {text: 'How are you', group: 'english'},
      {text: 'Tere', group: 'estonian'},
    ]

I would like to iterate over an array and create another array with categorized objects

    groups: any[] = [
    [
      { name: 'english', 
        items: [
           {text: 'Hello', group: 'english'},
           {text: 'How are you', group: 'english'},
        ]
      },
      { name: 'espanian', 
        items: [
           {text: 'Hola', group: 'espanian'}
        ]
      },
      { name: 'estonian', 
        items: [
           {text: 'Tere', group: 'estonian'}
        ]
      },
    ]

What is the most dynamic way to do it? The number of groups could be over 30, so I would like to avoid doing if or equals checks inside the loop

I mean dynamic - is basically without any explicit checks like item.group = 'english'

I have tried to achieve something similar with

      let result: any = [];
      this.listOfItems.forEach(p => {
        var key = p.group;
        result[key] = result[key] || [];
        result[key].push(p);
      })

however the result is not exactly what I am looking for

[
   {'english': [
             {text: 'Hello', group: 'english'},
             {text: 'How are you', group: 'english'},
            ]
   }, 
   {'espanian': [
             {text: 'Hola', group: 'espanian'},
            ]
   },
]
2
  • What does "most dynamic way" mean? Could you explain it a bit? Also, what have you tried so far? Commented Mar 29, 2022 at 11:06
  • @LazarNikolic answered your question in my post. I hope it makes it clear Commented Mar 29, 2022 at 11:14

4 Answers 4

5
  1. Why any[]?
  2. Maps are really great for these types of data manipulations. Check it out:
interface Item {
    text: string;
    group: string;
}

interface Categorized {
    name: string;
    items: Item[];
}

const itemList: Item[] = [
    { text: 'Hola', group: 'espanian' },
    { text: 'Hello', group: 'english' },
    { text: 'How are you', group: 'english' },
    { text: 'Tere', group: 'estonian' },
];

const categorize = (list: Item[]): Categorized[] => {
    const map: Record<string, Categorized> = {};

    list.forEach((item) => {
        if (!map[item.group]) {
            map[item.group] = { name: item.group, items: [item] };
            return;
        }

        map[item.group].items.push(item);
    });

    return Object.values(map);
};

console.log(categorize(itemList));

We're only using one loop.

Compiled:

var itemList = [
    { text: 'Hola', group: 'espanian' },
    { text: 'Hello', group: 'english' },
    { text: 'How are you', group: 'english' },
    { text: 'Tere', group: 'estonian' },
];
var categorize = function (list) {
    var map = {};
    list.forEach(function (item) {
        if (!map[item.group]) {
            map[item.group] = { name: item.group, items: [item] };
            return;
        }
        map[item.group].items.push(item);
    });
    return Object.values(map);
};
console.log(categorize(itemList));

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

7 Comments

But you're not using a map and this could be replaced with a reduce
@locrizak It could be replaced with a reduce. And a JS object is just a map under the hood, so I am technically using a map here.
Map and a plain object are not the same thing and you are not using a Map here either. Someone viewing this answer may not know they are the same concept but different syntax and functionality. You should be more clear and concise when answering questions.
@locrizak I'm not saying that a JavaScript Map and a JavaScript object are the same exact thing. I am saying that a JavaScript object is a "map"/"hash" data structure, because that's exactly what it is.
But they are different data structures. Other than being stored as key: value and having prototypes, they do different things and have different data structures. In this case calling map.size() will not work because it is an object. Saying Maps are really great for these types of data manipulations is maybe a correct statement (depending on the team) but you didn't actually use a map.
|
4

Array.reduce implementation.

const listOfItems = [
  { text: 'Hola', group: 'espanian' },
  { text: 'Hello', group: 'english' },
  { text: 'How are you', group: 'english' },
  { text: 'Tere', group: 'estonian' },
];

const output = listOfItems.reduce((acc, curr) => {
  const matchItem = acc.find(item => item.name === curr.group);
  if (matchItem) {
    matchItem.items.push(curr);
  } else {
    acc.push({
      name: curr.group,
      items: [curr]
    })
  }
  return acc;
}, []);
console.log(output);

For those who are concerned about double loop, you can achieve the above one with a single loop using.

const listOfItems = [
  { text: 'Hola', group: 'espanian' },
  { text: 'Hello', group: 'english' },
  { text: 'How are you', group: 'english' },
  { text: 'Tere', group: 'estonian' },
];

const output = listOfItems.reduce((acc, curr) => {
  acc[curr.group] ? acc[curr.group].items.push(curr) : acc[curr.group] = {
    name: curr.group,
    items: [curr]
  };
  return acc;
}, {});
console.log(Object.values(output));

2 Comments

With this you're using two loops though :( .find() is also a loop. Nested loops aren't necessary here. Check my solution, or Amaarrockz
@mstephen19 This is only the presentation of the core logic. If you want to see an implementation with single loop, you can check my updated answer.
1

Try with the below code

const modifiedList = listOfItems.reduce((acc, item) => {
 if(!acc[item.group]) acc[item.group]=[item];
 else acc[item.group].push(item);
 return acc;
}, {});
let newList = [];
for(let item in modifiedList) {
 newList.push({
   name: item,
   items: modifiedList[item]
 })
}

2 Comments

I really like this solution. Same thing as mine, but more concise. Nice!!!!
Incorrect usage of Array.reduce. Run this in local and check the console errors.
0

const listOfItems = [
    {text: 'Hola', group: 'espanian'},
    {text: 'Hello', group: 'english'},
    {text: 'How are you', group: 'english'},
    {text: 'Tere', group: 'estonian'},
]

const groups = listOfItems.reduce((acc, {text, group}) => {
    if(!acc[group]) {
        acc[group] = {name: group, items: []}
    }
    acc[group].items.push({text, group})
    return acc;
}, {});
console.log(Object.values(groups));

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.