網站搜尋

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"
  }]
}