Javascript group and array recursively by multiple properties in a specific order











up vote
0
down vote

favorite












I'm building a grid component that will allow the user to do multiple row grouping.



The original data I'm working on is as such example for stock items:



let stock = [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
];


What I need to accomplish is to be able to group that data using multiple fields in different orders, like the following results:



let result = groupBy(stock, ["order"]);

[
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
];

let result = groupBy(stock, ["item"]);

[
{
field: "item",
value: "IT282",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
},
{
field: "item",
value: "IT283",
rows: [
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "production", qty: 210, item: "IT283" }
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "210", type: "production", qty: 145, item: "IT233" }
]
}
];

let result = groupBy(stock, ["order", "item"]);

[
{
field: "order",
value: "200",
rows: [
{
field: "item",
value: "IT282",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "200",
type: "production",
qty: 45,
item: "IT233"
}
]
}
]
},
{
field: "order",
value: "210",
rows: [
{
field: "item",
value: "IT282",
rows: [{ order: "210", type: "stock", qty: 130, item: "IT282" }]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];

let result = groupBy(stock, ["item", "order"]);

[
{
field: "item",
value: "IT282",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
},
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
}
]
},
{
field: "item",
value: "IT102",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
}
]
},
{
field: "item",
value: "IT233",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];


My group function would receive any array of items and group any number of array fields in any order.



As I want an ES6 based function, I'm today using the following groupBy function from that link that works for a single pass, but I'm having difficulties nesting then together.



Here is the code I'm working on:



  groupBy = (rows, groups) => {
if (groups.length === 0) return rows;

return this.groupByField(rows, groups, 0);
};

groupByField = (rows, groups, index) => {
if (index >= groups.length) return rows;

let grouped = this.groupRows(rows, groups[index]);

index++;
let ret = ;

grouped.rows.map(row => {
ret.push(this.groupByField(row.rows, groups, index));
});

grouped.rows = ret;
return grouped;
};

groupRows = (rows, field) => {
return rows.reduce(function(groups, x) {
let val = helper.getValueByFieldNameString(x.data, field);

let found = groups.find(item => {
return item.groupedValue === val;
});

if (!found) {
let rows = ;
rows.push(x);

groups.push({
groupedField: field,
groupedValue: val,
rows: rows
});
} else {
found.rows.push(x);
}

return groups;
}, );
};


Seems that the recursivity is not working properly.










share|improve this question
























  • Why did you delete the previous one?
    – Ankit Agarwal
    Oct 24 at 13:25










  • stackoverflow.com/questions/52969950/…
    – Ankit Agarwal
    Oct 24 at 13:25










  • @Ankit, The previous one was wrong and I got 2 downvotes while editing it...
    – Mendes
    Oct 24 at 13:28















up vote
0
down vote

favorite












I'm building a grid component that will allow the user to do multiple row grouping.



The original data I'm working on is as such example for stock items:



let stock = [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
];


What I need to accomplish is to be able to group that data using multiple fields in different orders, like the following results:



let result = groupBy(stock, ["order"]);

[
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
];

let result = groupBy(stock, ["item"]);

[
{
field: "item",
value: "IT282",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
},
{
field: "item",
value: "IT283",
rows: [
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "production", qty: 210, item: "IT283" }
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "210", type: "production", qty: 145, item: "IT233" }
]
}
];

let result = groupBy(stock, ["order", "item"]);

[
{
field: "order",
value: "200",
rows: [
{
field: "item",
value: "IT282",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "200",
type: "production",
qty: 45,
item: "IT233"
}
]
}
]
},
{
field: "order",
value: "210",
rows: [
{
field: "item",
value: "IT282",
rows: [{ order: "210", type: "stock", qty: 130, item: "IT282" }]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];

let result = groupBy(stock, ["item", "order"]);

[
{
field: "item",
value: "IT282",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
},
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
}
]
},
{
field: "item",
value: "IT102",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
}
]
},
{
field: "item",
value: "IT233",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];


My group function would receive any array of items and group any number of array fields in any order.



As I want an ES6 based function, I'm today using the following groupBy function from that link that works for a single pass, but I'm having difficulties nesting then together.



Here is the code I'm working on:



  groupBy = (rows, groups) => {
if (groups.length === 0) return rows;

return this.groupByField(rows, groups, 0);
};

groupByField = (rows, groups, index) => {
if (index >= groups.length) return rows;

let grouped = this.groupRows(rows, groups[index]);

index++;
let ret = ;

grouped.rows.map(row => {
ret.push(this.groupByField(row.rows, groups, index));
});

grouped.rows = ret;
return grouped;
};

groupRows = (rows, field) => {
return rows.reduce(function(groups, x) {
let val = helper.getValueByFieldNameString(x.data, field);

let found = groups.find(item => {
return item.groupedValue === val;
});

if (!found) {
let rows = ;
rows.push(x);

groups.push({
groupedField: field,
groupedValue: val,
rows: rows
});
} else {
found.rows.push(x);
}

return groups;
}, );
};


Seems that the recursivity is not working properly.










share|improve this question
























  • Why did you delete the previous one?
    – Ankit Agarwal
    Oct 24 at 13:25










  • stackoverflow.com/questions/52969950/…
    – Ankit Agarwal
    Oct 24 at 13:25










  • @Ankit, The previous one was wrong and I got 2 downvotes while editing it...
    – Mendes
    Oct 24 at 13:28













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I'm building a grid component that will allow the user to do multiple row grouping.



The original data I'm working on is as such example for stock items:



let stock = [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
];


What I need to accomplish is to be able to group that data using multiple fields in different orders, like the following results:



let result = groupBy(stock, ["order"]);

[
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
];

let result = groupBy(stock, ["item"]);

[
{
field: "item",
value: "IT282",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
},
{
field: "item",
value: "IT283",
rows: [
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "production", qty: 210, item: "IT283" }
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "210", type: "production", qty: 145, item: "IT233" }
]
}
];

let result = groupBy(stock, ["order", "item"]);

[
{
field: "order",
value: "200",
rows: [
{
field: "item",
value: "IT282",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "200",
type: "production",
qty: 45,
item: "IT233"
}
]
}
]
},
{
field: "order",
value: "210",
rows: [
{
field: "item",
value: "IT282",
rows: [{ order: "210", type: "stock", qty: 130, item: "IT282" }]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];

let result = groupBy(stock, ["item", "order"]);

[
{
field: "item",
value: "IT282",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
},
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
}
]
},
{
field: "item",
value: "IT102",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
}
]
},
{
field: "item",
value: "IT233",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];


My group function would receive any array of items and group any number of array fields in any order.



As I want an ES6 based function, I'm today using the following groupBy function from that link that works for a single pass, but I'm having difficulties nesting then together.



Here is the code I'm working on:



  groupBy = (rows, groups) => {
if (groups.length === 0) return rows;

return this.groupByField(rows, groups, 0);
};

groupByField = (rows, groups, index) => {
if (index >= groups.length) return rows;

let grouped = this.groupRows(rows, groups[index]);

index++;
let ret = ;

grouped.rows.map(row => {
ret.push(this.groupByField(row.rows, groups, index));
});

grouped.rows = ret;
return grouped;
};

groupRows = (rows, field) => {
return rows.reduce(function(groups, x) {
let val = helper.getValueByFieldNameString(x.data, field);

let found = groups.find(item => {
return item.groupedValue === val;
});

if (!found) {
let rows = ;
rows.push(x);

groups.push({
groupedField: field,
groupedValue: val,
rows: rows
});
} else {
found.rows.push(x);
}

return groups;
}, );
};


Seems that the recursivity is not working properly.










share|improve this question















I'm building a grid component that will allow the user to do multiple row grouping.



The original data I'm working on is as such example for stock items:



let stock = [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
];


What I need to accomplish is to be able to group that data using multiple fields in different orders, like the following results:



let result = groupBy(stock, ["order"]);

[
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "production", qty: 210, item: "IT283" },
{ order: "210", type: "customer", qty: 230, item: "IT102" },
{ order: "210", type: "production", qty: 145, item: "IT233" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
];

let result = groupBy(stock, ["item"]);

[
{
field: "item",
value: "IT282",
rows: [
{ order: "200", type: "production", qty: 200, item: "IT282" },
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{ order: "210", type: "production", qty: 300, item: "IT282" },
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
},
{
field: "item",
value: "IT283",
rows: [
{ order: "200", type: "production", qty: 90, item: "IT283" },
{ order: "200", type: "production", qty: 110, item: "IT283" },
{ order: "210", type: "production", qty: 190, item: "IT283" },
{ order: "210", type: "production", qty: 210, item: "IT283" }
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" },
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" },
{ order: "210", type: "production", qty: 145, item: "IT233" }
]
}
];

let result = groupBy(stock, ["order", "item"]);

[
{
field: "order",
value: "200",
rows: [
{
field: "item",
value: "IT282",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" },
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "200",
type: "production",
qty: 45,
item: "IT233"
}
]
}
]
},
{
field: "order",
value: "210",
rows: [
{
field: "item",
value: "IT282",
rows: [{ order: "210", type: "stock", qty: 130, item: "IT282" }]
},
{
field: "item",
value: "IT283",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
},
{
field: "item",
value: "IT102",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
},
{
field: "item",
value: "IT233",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];

let result = groupBy(stock, ["item", "order"]);

[
{
field: "item",
value: "IT282",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 200,
item: "IT282"
},
{ order: "200", type: "stock", qty: 30, item: "IT282" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 300,
item: "IT282"
},
{ order: "210", type: "stock", qty: 130, item: "IT282" }
]
}
]
},
{
field: "item",
value: "IT283",
rows: [
{
field: "order",
value: "200",
rows: [
{
order: "200",
type: "production",
qty: 90,
item: "IT283"
},
{
order: "200",
type: "production",
qty: 110,
item: "IT283"
}
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 190,
item: "IT283"
},
{
order: "210",
type: "production",
qty: 210,
item: "IT283"
}
]
}
]
},
{
field: "item",
value: "IT102",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "customer", qty: 80, item: "IT102" },
{ order: "200", type: "customer", qty: 130, item: "IT102" }
]
},
{
field: "order",
value: "210",
rows: [
{ order: "210", type: "customer", qty: 180, item: "IT102" },
{ order: "210", type: "customer", qty: 230, item: "IT102" }
]
}
]
},
{
field: "item",
value: "IT233",
rows: [
{
field: "order",
value: "200",
rows: [
{ order: "200", type: "production", qty: 45, item: "IT233" }
]
},
{
field: "order",
value: "210",
rows: [
{
order: "210",
type: "production",
qty: 145,
item: "IT233"
}
]
}
]
}
];


My group function would receive any array of items and group any number of array fields in any order.



As I want an ES6 based function, I'm today using the following groupBy function from that link that works for a single pass, but I'm having difficulties nesting then together.



Here is the code I'm working on:



  groupBy = (rows, groups) => {
if (groups.length === 0) return rows;

return this.groupByField(rows, groups, 0);
};

groupByField = (rows, groups, index) => {
if (index >= groups.length) return rows;

let grouped = this.groupRows(rows, groups[index]);

index++;
let ret = ;

grouped.rows.map(row => {
ret.push(this.groupByField(row.rows, groups, index));
});

grouped.rows = ret;
return grouped;
};

groupRows = (rows, field) => {
return rows.reduce(function(groups, x) {
let val = helper.getValueByFieldNameString(x.data, field);

let found = groups.find(item => {
return item.groupedValue === val;
});

if (!found) {
let rows = ;
rows.push(x);

groups.push({
groupedField: field,
groupedValue: val,
rows: rows
});
} else {
found.rows.push(x);
}

return groups;
}, );
};


Seems that the recursivity is not working properly.







javascript ecmascript-6 grouping






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 10 at 19:21









halfer

14.2k757106




14.2k757106










asked Oct 24 at 13:24









Mendes

4,357956131




4,357956131












  • Why did you delete the previous one?
    – Ankit Agarwal
    Oct 24 at 13:25










  • stackoverflow.com/questions/52969950/…
    – Ankit Agarwal
    Oct 24 at 13:25










  • @Ankit, The previous one was wrong and I got 2 downvotes while editing it...
    – Mendes
    Oct 24 at 13:28


















  • Why did you delete the previous one?
    – Ankit Agarwal
    Oct 24 at 13:25










  • stackoverflow.com/questions/52969950/…
    – Ankit Agarwal
    Oct 24 at 13:25










  • @Ankit, The previous one was wrong and I got 2 downvotes while editing it...
    – Mendes
    Oct 24 at 13:28
















Why did you delete the previous one?
– Ankit Agarwal
Oct 24 at 13:25




Why did you delete the previous one?
– Ankit Agarwal
Oct 24 at 13:25












stackoverflow.com/questions/52969950/…
– Ankit Agarwal
Oct 24 at 13:25




stackoverflow.com/questions/52969950/…
– Ankit Agarwal
Oct 24 at 13:25












@Ankit, The previous one was wrong and I got 2 downvotes while editing it...
– Mendes
Oct 24 at 13:28




@Ankit, The previous one was wrong and I got 2 downvotes while editing it...
– Mendes
Oct 24 at 13:28












1 Answer
1






active

oldest

votes

















up vote
2
down vote



accepted










I think you can just make a function that groups an array using reduce and Object.values. Then after you group the array, if you have more fields to group by, call the function on each child row array. For example:






let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)








share|improve this answer























  • Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
    – Mendes
    Oct 24 at 23:10












  • Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
    – Mark Meyer
    Oct 25 at 0:27






  • 1




    Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
    – Mendes
    Oct 25 at 22:54










  • one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
    – Mendes
    Oct 25 at 23:53













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52970177%2fjavascript-group-and-array-recursively-by-multiple-properties-in-a-specific-orde%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote



accepted










I think you can just make a function that groups an array using reduce and Object.values. Then after you group the array, if you have more fields to group by, call the function on each child row array. For example:






let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)








share|improve this answer























  • Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
    – Mendes
    Oct 24 at 23:10












  • Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
    – Mark Meyer
    Oct 25 at 0:27






  • 1




    Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
    – Mendes
    Oct 25 at 22:54










  • one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
    – Mendes
    Oct 25 at 23:53

















up vote
2
down vote



accepted










I think you can just make a function that groups an array using reduce and Object.values. Then after you group the array, if you have more fields to group by, call the function on each child row array. For example:






let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)








share|improve this answer























  • Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
    – Mendes
    Oct 24 at 23:10












  • Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
    – Mark Meyer
    Oct 25 at 0:27






  • 1




    Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
    – Mendes
    Oct 25 at 22:54










  • one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
    – Mendes
    Oct 25 at 23:53















up vote
2
down vote



accepted







up vote
2
down vote



accepted






I think you can just make a function that groups an array using reduce and Object.values. Then after you group the array, if you have more fields to group by, call the function on each child row array. For example:






let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)








share|improve this answer














I think you can just make a function that groups an array using reduce and Object.values. Then after you group the array, if you have more fields to group by, call the function on each child row array. For example:






let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)








let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)





let stock = [{ order: "200", type: "production", qty: 200, item: "IT282" },{ order: "200", type: "production", qty: 90, item: "IT283" },{ order: "200", type: "customer", qty: 80, item: "IT102" },{ order: "200", type: "production", qty: 110, item: "IT283" },{ order: "200", type: "customer", qty: 130, item: "IT102" },{ order: "200", type: "production", qty: 45, item: "IT233" },{ order: "200", type: "stock", qty: 30, item: "IT282" },{ order: "210", type: "production", qty: 300, item: "IT282" },{ order: "210", type: "production", qty: 190, item: "IT283" },{ order: "210", type: "customer", qty: 180, item: "IT102" },{ order: "210", type: "production", qty: 210, item: "IT283" },{ order: "210", type: "customer", qty: 230, item: "IT102" },{ order: "210", type: "production", qty: 145, item: "IT233" },{ order: "210", type: "stock", qty: 130, item: "IT282" }];

function groupBy(arr, fields) {
let field = fields[0] // one field at a time
if (!field) return arr
let retArr = Object.values(
arr.reduce((obj, current) => {
if (!obj[current[field]]) obj[current[field]] = {field: field, value: current[field],rows: }
obj[current[field]].rows.push(current)
return obj
}, {}))

// recurse for each child's rows if there are remaining fields
if (fields.length){
retArr.forEach(obj => {
obj.count = obj.rows.length
obj.rows = groupBy(obj.rows, fields.slice(1))
})
}
return retArr
}

let result = groupBy(stock, ["order", "item"]);
console.log(result)






share|improve this answer














share|improve this answer



share|improve this answer








edited Oct 25 at 0:26

























answered Oct 24 at 19:42









Mark Meyer

30.9k32550




30.9k32550












  • Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
    – Mendes
    Oct 24 at 23:10












  • Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
    – Mark Meyer
    Oct 25 at 0:27






  • 1




    Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
    – Mendes
    Oct 25 at 22:54










  • one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
    – Mendes
    Oct 25 at 23:53




















  • Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
    – Mendes
    Oct 24 at 23:10












  • Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
    – Mark Meyer
    Oct 25 at 0:27






  • 1




    Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
    – Mendes
    Oct 25 at 22:54










  • one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
    – Mendes
    Oct 25 at 23:53


















Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
– Mendes
Oct 24 at 23:10






Thanks Mark for the post. I'm trying your posted code and for now it is working fine. I'm not stuck on buiding my table from the results.. How can I know, when rendering the rows, which colums to do rowSpan and for how many rows?
– Mendes
Oct 24 at 23:10














Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
– Mark Meyer
Oct 25 at 0:27




Hi @Mendes, there was actually a bug in that code, which I think I fixed. I also edited it to add a count to each item which will be the total of all descendants to a node, which is just the row length before recursing. I think that's the count you're after.
– Mark Meyer
Oct 25 at 0:27




1




1




Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
– Mendes
Oct 25 at 22:54




Very nice piece of code. Thanks @Mark for the version and attending my extra requirement.... Well done.
– Mendes
Oct 25 at 22:54












one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
– Mendes
Oct 25 at 23:53






one more question. How can I add to each row the recursive fields that it grouped ? In example, the last example should show on each row that it is grouped by field and order, as well as the number of elements. This is necessary to do colSpan's when building a result table using html. Thanks for helping!
– Mendes
Oct 25 at 23:53




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52970177%2fjavascript-group-and-array-recursively-by-multiple-properties-in-a-specific-orde%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Guess what letter conforming each word

Port of Spain

Run scheduled task as local user group (not BUILTIN)