0

I have the following array:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

I would like to have it grouped in this way:

[
    {
        'oneA': [
            { name: 'john', class: 'one', section: 'a' },
            { name: 'john3', class: 'one', section: 'a' }
        ]
    },
    {
        'oneB': [
            { name: 'john2', class: 'one', section: 'b' }
        ]
    },
    {
        'twoA': [
            { name: 'john4', class: 'two', section: 'a' }
        ]
    },
    {
        'twoB': [
            { name: 'john5', class: 'two', section: 'b' },
            { name: 'john6', class: 'two', section: 'b' }
        ]
    },
    {
        'threeA': [
            { name: 'john7', class: 'three', section: 'a' }
        ]
    },
    {
        'fourA': [
            { name: 'john8', class: 'four', section: 'a' }
        ]
    },
    {
        'fourB': [
            { name: 'john9', class: 'four', section: 'b' }
        ]
    }
]

I have tried something like this:

items.sort(function (a, b) {
  if (a.class > b.class) return 1;
  if (a.class < b.class) return -1;

  if (a.section > b.section) return 1;
  if (a.section < b.section) return -1;
})

This orders the array as I want but it is not grouping as I described above. Do you know a way to reach that?

6
  • Why does your result need to contain an array of objects that each have a single key? Why not just one object with multiple keys? And what's the sorting logic? Are you trying to sort based on the text representation of a number? Commented Jan 28, 2020 at 9:03
  • @RobbyCornelissen I need an array of objects because I will loop on it and then I will loop in every array of items. Commented Jan 28, 2020 at 9:06
  • 1
    Possible duplicate of stackoverflow.com/a/14484332/5611099 Commented Jan 28, 2020 at 9:12
  • Does this answer your question? Most efficient method to groupby on an array of objects Commented Jan 28, 2020 at 9:18
  • Are you sure that the below node has 'three' class value also ? ``` { 'fourA': [ { name: 'john7', class: 'three', section: 'a' }, { name: 'john8', class: 'four', section: 'a' } ] }``` Commented Jan 28, 2020 at 9:20

5 Answers 5

2

You could just group them by taking a Map.

It generates a key of class and uppercase section, tries to get the value from the map or take an empty array for spreding int a new array with the actual object. Then it sets this array as new value in the map.

Array.from gets all key/value pairs from the map and build new objects with a computed property name.

const
    getKey = o => `${o.class}${o.section.toUpperCase()}`,
    items = [{ name: 'john', class: 'one', section: 'a' }, { name: 'john2', class: 'one', section: 'b' }, { name: 'john3', class: 'one', section: 'a' }, { name: 'john4', class: 'two', section: 'a' }, { name: 'john5', class: 'two', section: 'b' }, { name: 'john6', class: 'two', section: 'b' }, { name: 'john7', class: 'three', section: 'a' }, { name: 'john8', class: 'four', section: 'a' }, { name: 'john9', class: 'four', section: 'b' }],
    result = Array.from(
        items.reduce(
            (m, o) => m.set(getKey(o), [...(m.get(getKey(o)) || []), o]),
            new Map
        ),
        ([k, v]) => ({ [k]: v })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

12 Comments

Could you please explain the code a little more in detail just for better understanding what it does?
Omg, I hope you don't code in production like this)
@NinaScholz i think there are few things, for example: you define items and result like global. And why do you define getKey if you can just concatenate strings in loop body. I'm not sure but I think you can use {} instead if new Map because it's more familiar for our community. I think you unusually solved the problem, but not in the best way
UPD: items and result are not global, i'm sorry :)
Yes, it works, but it's not familiar as @SaveliTomac said.
|
1
const items = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];

let newArray = items.reduce((main, curr) => {
    let index = main.findIndex(obj => Object.keys(obj).includes(curr.class + curr.section))
    if (index == -1) main.push({ [curr.class + curr.section]: [curr] })
    else main[index][curr.class + curr.section].push(curr)
    return main
}, [])
console.log(newArray)

1 Comment

0

You can use reduce function like:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

const groupedItems = items.reduce((result, current) => {
  const key = current.class + current.section.toUpperCase();
  
  if (Array.isArray(result[key])) {
    result[key].push(current);
  } else {
    result[key] = [current];
  }
  
  return result;
}, {});

const expectedResult = Array.from(Object.keys(groupedItems), (elem) => ({[elem]: groupedItems[elem]}));

console.log(expectedResult);

7 Comments

It works, but I'm not getting the same order then yours.
You can sort result of reduce, it's simple, i will update my answer
Ok, I did it. Anyway your solution generate an object instead of an array of objects as I described in my question.
@smartmouse your expected result is an array from an once element, you can get it like this: [groupedItems]
@smartmouse are you sure my stdoutput is not what you expect? Check it please again
|
0

I would suggest to omit the outer array of your result, because for me it does not make sense to have it if you want to create a set of objects.

To solve your problem, just iterate over all items and check if your result object already has the required key. If not, create it, and then insert the current item into the corresponding array.

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

let result = {};

items.forEach(item => {
  let key = `${item.class}${item.section.toUpperCase()}`;
  if(!result.hasOwnProperty(key)) {
    result[key] = [];
  }
  result[key].push(item);
});

console.info(result);

1 Comment

Why is this answer downvoted? In my opinion it provides the best explanation to a solution, compared to the other answers that just pasted a bunch of code.
0

If you are expecting answer in plain javascript for better understanding without using in built functions.

var items = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];
var resultArray =[];
for(var i=0;i<items.length;i++){
    if(resultArray[items[i]['class']+items[i]['section'].toUpperCase()]){
        resultArray[items[i]['class']+items[i]['section'].toUpperCase()].push(items[i]);
    } else {
        var a ="";
        a = items[i]['class']+items[i]['section'].toUpperCase();
        resultArray[a]=[];
        resultArray[a].push(items[i])
    }
}

console.log(resultArray);

enter image description here

2 Comments

It works, but the order of items is not as I described in my question.
Order is working fine, can you verify it again . I have now included the uppercase part of section which was missed earlier.. Thanks

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.