typescript - 使用 Angular 4 Pipes 或 Rxjs 運算子對物件陣列(json)中的屬性值進行分組/求和
我 HTTP 呼叫的回應是一個像這樣的公司物件陣列:
response = [{
"name": "CompanyName1",
"registrations": [
{
"country": "en",
"register": "registerType1",
"amount": 2
},
{
"country": "wa",
"register": "registerType2",
"amount": 1
},{
"country": "en",
"register": "registerType1",
"amount": 3
},
{
"country": "sc",
"register": "registerType3",
"amount": 2
},
{
"country": "wa",
"register": "registerType3",
"amount": 2
}
]
},
{
"name": "CompanyName2",
"registrations": [
{
"country": "wa",
"register": "registerType1",
"amount": 3
},
{
"country": "sc",
"register": "registerType3",
"amount": 1
}
]]
我需要按登記機構和國家將公司的註冊資料分組/求和到一個新的屬性名稱(registerType_12),如果 registerType1 或 registerType2,否則就保持屬性名稱為 registerTypeN。對於 companyName1,我需要得到像這樣的結果:
CompanyName1: [{country:en, registerType_12, amount:5},
{country:wa, registerTyoe_12, amount:1,
{country:sc, registerType3, amount:4}
}]
答案#1
我做了一個 Plunkr 來示範如何做這件事: https://plnkr.co/edit/uO48fEVJGy2nwm587PO8?p=preview
這裡有一個解釋
我把你的資料儲存在一個變數中:
const { Observable } = Rx;
// FAKE DATA FROM BACKEND
const data = [
{
"name": "CompanyName1",
"registrations": [
{
"country": "en",
"register": "registerType1",
"amount": 2
},
...
]
},
{
"name": "CompanyName2",
"registrations": [
{
"country": "wa",
"register": "registerType1",
"amount": 3
},
...
]
}];
我為 Response 和 Http.get 方法做了一個模擬,所以最後我們有一些看起來真的像 Angular 代碼的東西:
(這些與你的問題並不直接相關)
// MOCK RESPONSE OBJECT
const response = () => {
const r = {
body: null
};
r.json = () => JSON.parse(r.body);
return r;
};
// MOCK HTTP METHOD
const http = {
get: (url) => {
const res = response();
res.body = JSON.stringify(data);
return Observable.of(res).delay(1000)
}
};
以下是你可以以乾淨的方式解決問題的方法:
http
.get('some-url')
.map(res => res.json())
.map(groupByCompanyNameAndRegistration)
.do(data => console.log(data))
.subscribe();
顯然,
groupByCompanyNameAndRegistration 還沒有被建立。但我們有一個乾淨的 Observable 流,真的很容易理解發生了什麼。程式碼的邏輯部分:
const flattenObj = (obj) =>
Object.keys(obj).map(key => obj[key]);
const groupByCompanyNameAndRegistration = (data) => {
return data.reduce((accComp, company) => {
if (!accComp[company.name]) {
accComp[company.name] = {};
}
const compObj = company.registrations.reduce((accReg, reg) => {
if (!accReg[reg.country]) {
accReg[reg.country] = {
country: reg.country,
amount: 0
};
}
accReg[reg.country].amount += reg.amount;
return accReg;
}, {});
accComp[company.name] = flattenObj(compObj);
return accComp;
}, {});
};
最後,控制台輸出:
{
"CompanyName1": [
{
"country": "en",
"amount": 5
},
{
"country": "wa",
"amount": 3
},
{
"country": "sc",
"amount": 2
}
],
"CompanyName2": [
{
"country": "wa",
"amount": 3
},
{
"country": "sc",
"amount": 1
}
]
}
答案#2
你不需要 TypeScript 或 RxJS 就能做到這一點。這可以用純 JavaScript 完成。
JSBin 連結 : https://jsbin.com/yujocofesu/edit?js,console
response.reduce((finalObj, obj)=>{
finalObj[obj.name] =
obj.registrations
.map(reg => Object.assign(reg,
{register: (
reg.register ==='registerType1'||
reg.register==='registerType2') ?
'registerType_12': reg.register
})
)
.reduce(mergeSimilar, []);
return finalObj;
},{});
function mergeSimilar(newList, item){
let idx = newList.findIndex(
i=> i.country === item.country && i.register === item.register);
if(idx>-1){
newList[idx].amount += item.amount;
}else{
newList.push(item)
}
return newList;
}
這給出回應為
{
CompanyName1: [{
amount: 5,
country: "en",
register: "registerType_12"
}, {
amount: 1,
country: "wa",
register: "registerType_12"
}, {
amount: 2,
country: "sc",
register: "registerType3"
}, {
amount: 2,
country: "wa",
register: "registerType3"
}],
CompanyName2: [{
amount: 3,
country: "wa",
register: "registerType_12"
}, {
amount: 1,
country: "sc",
register: "registerType3"
}]
}
