2

I am trying to transform an array of objects to a 'grouped' and sorted output array in JavaScript, preferably the ES6-way (e.g. using .map, .reduce, etc.).

Summary:

  • array of objects
  • each object is an 'item': a1,a2,b1,b2,...
  • each object has a 'section' property which is like a group/category
  • the desired output array should return the item objects grouped by 'section'
  • the sorting order of the output arrays of sections and items is defined in a property as well
const data = [
    { section: 'A', item: 'a1', section_order: 2, item_order: 2 },
    { section: 'B', item: 'b1', section_order: 1, item_order: 2 },
    { section: 'A', item: 'a2', section_order: 2, item_order: 1 },
    { section: 'B', item: 'b2', section_order: 1, item_order: 1 }
];


const desiredOutput = [
    {
        section: 'B',  // should come first as section_order = 1
        items: [
            { section: 'B', item: 'b2', section_order: 1, item_order: 1 },  // should come first as item_order = 1
            { section: 'B', item: 'b1', section_order: 1, item_order: 2 },   // should come second as item_order = 2
        ]
    },
    {
        section: 'A',   // should come second as section_order = 2
        items: [
            { section: 'A', item: 'a2', section_order: 2, item_order: 1 },
            { section: 'A', item: 'a1', section_order: 2, item_order: 2 },
        ]
    }
];

I saw an example that gets me somewhere in the right direction - but not exactly to the desired output and without catering for the sorting-order:

const result = data.reduce((accum, currElm) => {
    const currSection = currElm['section'];
    accum[currSection] = (accum[currSection] || []).concat(currElm);
    return accum;
}, {});

1 Answer 1

1

You should sort your data first. The sort function look like this

data.sort((a, b) => (a.section_order - b.section_order) || (a.item_order - b.item_order));

As you can see, we split the condition into 2 parts:

  1. (a.section_order - b.section_order)
  2. (a.item_order - b.item_order)

It means that you sort section_order then by item_order.

After that, you can loop just one time by using dictionary power like below

const data = [
    { section: 'A', item: 'a1', section_order: 2, item_order: 2 },
    { section: 'B', item: 'b1', section_order: 1, item_order: 2 },
    { section: 'A', item: 'a2', section_order: 2, item_order: 1 },
    { section: 'B', item: 'b2', section_order: 1, item_order: 1 }
];
data.sort((a, b) => (a.section_order - b.section_order) || (a.item_order - b.item_order));

var result = [];
for(var item of data){
  var section = item.section;
  if(!result[section]){ // if not exists => create new 
    result[section] = { section, items:[item] };
  }else{ // if exists => add one more item into items 
   result[section].items.push(item);
  }
}
console.log(Object.values(result));

Old version using reduce & map

const data = [
    { section: 'A', item: 'a1', section_order: 2, item_order: 2 },
    { section: 'B', item: 'b1', section_order: 1, item_order: 2 },
    { section: 'A', item: 'a2', section_order: 2, item_order: 1 },
    { section: 'B', item: 'b2', section_order: 1, item_order: 1 }
];
data.sort((a, b) => (a.section_order - b.section_order) || (a.item_order - b.item_order));

const accumData = data.reduce((accum, currElm) => {
    const currSection = currElm['section'];
    accum[currSection] = (accum[currSection] || []).concat(currElm);
    return accum;
}, {}); 


var result = Object.entries(accumData).map(([key, value]) => ({section: key, items: value}));
console.log(result);

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

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.