[英]Group sum and transform json object with values in nested array
我試圖聚合和轉換以下json:
[
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}]
到按itemId分組的對象,然后按數量匯總,並按月總計成本(項目成本*每個訂單的項目數量)匯總。 例:
[
{
"itemId": 100,
"period": [
{
"month": "01/17",
"quantity": 12,
"cost": 130
}
]
},
{
"itemId": 101,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 100
},
{
"month": "02/17",
"quantity": 5,
"cost": 100
}
]
},
{
"itemId": 102,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 125
},
{
"month": "02/17",
"quantity": 1,
"cost": 25
}
]
}
]
我的桌子上有一個小的凹痕,我一直在試圖用我的原始map / reduce或lodash來計算如何做到這一點。
你可以這樣做:
var orders = [{orderId:"01",date:"2017-01-02T06:00:00.000Z",items:[{itemId:100,itemCost:12,itemQuantity:10},{itemId:102,itemCost:25,itemQuantity:4}]},{orderId:"02",date:"2017-01-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]},{orderId:"03",date:"2017-02-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]}]; // First, map your orders by items var items = {}; orders.forEach(function(order) { // set the month of each order var month = new Date(order.date); month = ('0' + (month.getMonth() + 1)).slice(-2) + '/' + String(month.getFullYear()).slice(-2); // for each item in this order order.items.forEach(function(item) { // here we already have both keys: "id" and "month" // then, we make sure they have an object to match var id = item.itemId; if (!items[id]) { items[id] = {}; } if (!items[id][month]) { items[id][month] = { cost:0, quantity:0 }; } // keep calculating the total cost items[id][month].cost += item.itemCost * item.itemQuantity; items[id][month].quantity += item.itemQuantity; }); }); // Now, we format the calculated values to your required output: var result = Object.keys(items).map(function(id) { var obj = { itemId: id, period: Object.keys(items[id]).map(function(month) { items[id][month].month = month; return items[id][month]; }), }; return obj; }); console.log(result);
希望能幫助到你。
您可以使用此轉換:
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
const myList = [ { "orderId" : "01", "date" : "2017-01-02T06:00:00.000Z", "items" : [ { "itemId": 100, "itemCost": 12, "itemQuantity": 10 }, { "itemId": 102, "itemCost": 25, "itemQuantity": 4 } ] }, { "orderId": "02", "date" : "2017-01-08T06:00:00.000Z", "items" : [ { "itemId": 100, "itemCost": 15, "itemQuantity": 2 }, { "itemId": 101, "itemCost": 20, "itemQuantity": 5 }, { "itemId": 102, "itemCost": 25, "itemQuantity": 1 } ] }, { "orderId": "03", "date" : "2017-02-08T06:00:00.000Z", "items" : [ { "itemId": 100, "itemCost": 15, "itemQuantity": 2 }, { "itemId": 101, "itemCost": 20, "itemQuantity": 5 }, { "itemId": 102, "itemCost": 25, "itemQuantity": 1 } ] }]; const result = Object.values(myList.reduce( (acc, o) => { const month = o.date.substr(5,2) + '/' + o.date.substr(2,2); return o.items.reduce ( (acc, item) => { const it = acc[item.itemId] || { itemId: item.itemId, period: {} }, m = it.period[month] || { month: month, quantity: 0, cost: 0 }; m.cost += item.itemCost * item.itemQuantity; m.quantity += item.itemQuantity; it.period[month] = m; acc[item.itemId] = it; return acc; }, acc); }, {})).map( o => Object.assign({}, o, { period: Object.values(o.period) }) ); console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
我認為那里的其他答案從香草的角度來看做得非常好,所以我想把它作為一個標簽,因為我想采取更多的lodash密集的方法。 這主要是一個有趣的挑戰,但我希望解決方案足夠優雅,您可以從中提升組件。
在開始之前,我將使用vanilla lodash模塊和lodash的函數式編程風格 。 設fp
是函數式編程模塊, _
是vanilla(讓orders
成為你的原始數據結構)。 此外,作為一項挑戰,我將盡最大努力減少vanilla JS方法和箭頭函數,以最大化lodash方法和函數創建方法。
首先,讓我們連續獲取所有項目,並與他們的訂單信息配對:
const items = _.flatMap(orders, o=> _.map(o.items, i=> [i, o]));
我知道我說我想最小化箭頭函數,但我想不出任何其他方法將訂單對象放到鏈的末尾。 挑戰自己在組合方面重寫上述內容(例如fp.compose
或_.flow
),看看會發生什么。
我要說現在和任何按照項目ID分組我們的對的時間一樣好:
const id_to_orders = _.groupBy(items, fp.get('[0].itemId'));
這里, fp.get('[0].itemId')
為我們提供了一個函數,給定一個數組,返回第一個元素的itemId
(在我們的例子中,我們有一個對列表,其中第一個元素是item,第二個是相關的訂單對象)。 因此, id_to_orders
是從項目ID到所有訂購時間列表的映射。
這個id_to_orders
映射看起來非常接近我們所追求的數據結構。 從高層次來看,剩下的就是將每個項目的訂單數據轉換為按月分組的數量和成本。
const result = _.mapValues(id_map, fp.flow(
// Arrange the item's orders into groups by month
fp.groupBy(month)
// We're done with the order objects, so fp.get('[0]') filters them
// out, and the second function pairs the item's cost and quantity
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
// Sum up the cost (left) and quantity (right) for the item for the month
, fp.reduce(add_pair, [0, 0])))
// These last couple lines just transform the resulting data to look
// closer to the desired structure.
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count}))
));
上面提到的助手month
和add_pair
:
function month([item, order]){
const date = new Date(order.date)
, month = date.getMonth() + 1
, year = date.getFullYear().toString().slice(-2);
return `${month}/${year}`;
}
function add_pair(p1, p2){
return [p1[0] + p2[0], p1[1] + p2[1]];
}
出於好奇(或虐待狂),讓我們看看這整個事物看起來像鏈接在一起作為一個管道:
const get_order_data = fp.flow(
fp.flatMap(o=> _.map(o.items, i=> [i, o]))
, fp.groupBy(fp.get('[0].itemId'))
, fp.mapValues(fp.flow(
fp.groupBy(month)
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
, fp.reduce(add_pair, [0, 0])))
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count})))
));
const result = get_order_data(orders);
你會注意到這個組合版本有更多的fp
(而不是_
)。 如果你很好奇為什么這樣更容易,我鼓勵你閱讀lodash FP指南 。
jsfiddle與一切。
最后,如果您想將上面代碼中的結果完全轉換為您在帖子中提到的輸出格式,這就是我的建議:
const formatted = _.keys(result).map(k=> ({itemId: k, periods: result[k]}));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.