Reputation: 2179
I have an array with different objects where each object is an order. Looks like this:
array = [
{
doc : {
date: "2017-03-06T16:48:23.080Z",
products:[
{
product: "Product 1",
number: 3
},
{
product: "Product 2",
number: 2
}
]
}
},
{
doc : {
date: "2017-03-07T16:48:23.080Z",
products:[
{
product: "Product 2",
number: 10
},
{
product: "Product 3",
number: 1
}
]
}
}
]
I want to be able to take this data and push it into new arrays, where each one has a date property (grouping by date), and a product nested array, which groups the different orders (grouping by product). Like this:
array = [
{
date: 'Date X',
products: [
{
product: 'product 1',
units: 3
},
{
product: 'product 2',
units: 2
}
]
},
{
date: 'Date Y',
products: [
{
product: 'product 2',
units: 10
},
{
product: 'product 3',
units: 1
}
]
}
]
How would you approach this? I've tried with Lodash trying to reuse some old code with no luck so far. Could it be done with a forEach?
Upvotes: 1
Views: 162
Reputation: 6840
Solution using lodash and ES6:
// Remove unnecesary .doc properties
let arrayWithoutDoc = _.map(array, el => el.doc);
// Group by dates to single object
let grouped = _.groupBy(arrayWithoutDoc, el => el.date);
// Convert object to requested format
let result = _.map(grouped, (value, date) => ({
date: date,
products: sumProductsUnits(
_.flatMap(value, el => el.products)
)
}));
// Function for grouping unique products with sum of units
function sumProductsUnits(products) {
var productGroups = _.groupBy(products, productObj => productObj.product);
return _.map(productGroups, (productGroup, productName) => ({
product: productName,
units: productGroup.reduce((sum, val) => sum + val.number, 0)
}))
}
I assumed, that units
equals number
in orignal object.
result
contains:
[
{
"date":"2017-03-06T16:48:23.080Z",
"products":[
{
"product":"Product 1",
"units":3
},
{
"product":"Product 2",
"units":2
}
]
},
{
"date":"2017-03-07T16:48:23.080Z",
"products":[
{
"product":"Product 2",
"units":10
},
{
"product":"Product 3",
"units":1
}
]
}
]
Upvotes: 1
Reputation: 386883
You could use a nested approach for a hash table which reflects the date and products for easy access.
var array = [{ doc: { date: "2017-03-06T16:48:23.080Z", products: [{ product: "Product 1", number: 3 }, { product: "Product 2", number: 2 }] } }, { doc: { date: "2017-03-07T16:48:23.080Z", products: [{ product: "Product 2", number: 10 }, { product: "Product 3", number: 1 }] } }],
result = [],
hash = { _: result };
array.forEach(function (a) {
var date = a.doc.date.slice(0, 10),
temp = hash;
if (!temp[date]) {
temp[date] = { _: [] };
temp._.push({ date: date, products: temp[date]._ });
}
temp = temp[date];
a.doc.products.forEach(function (b) {
if (!temp[b.product]) {
temp[b.product] = { product: b.product, units: 0 };
temp._.push(temp[b.product]);
}
temp[b.product].units += b.number;
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 1
Reputation: 1039538
A trivial algorithm would be to store the results into an array and check for the presence of the date in this array on each iteration:
function groupByDate(array) {
var result = [];
for (var i = 0; i < array.length; i++) {
var doc = array[i].doc;
// try to find an element in the resulting array with the same date
var group = result.find(function(element) {
// Note: this will do string comparison up to the millisecond.
// If you want to group by days, months or years you might need
// to parse the dates here
return element.doc.date == doc.date;
}) || null;
if (group == null) {
// No element with the specified date was found in the resulting array =>
// we create a new one and append it
group = {
doc: {
date: doc.date,
products: []
}
};
result.push(group);
}
// push the input products to the group
group.doc.products.push.apply(group.doc.products, doc.products);
}
return result;
}
and then use this function like this:
var array = ... the input array from your question ...
var reuslt = groupByDate(array);
Upvotes: 0