简体   繁体   中英

How to convert json into a javascript object, with grouping a key

I have a json like below

[{"AssetClass":"Macro","Name":"China. Inflation","IlliquidYears":1,"Turnover":0.4},
{"AssetClass":"Equity","Name":"Japan Equity","IlliquidYears":1,"Turnover":0.3},
{"AssetClass":"Equity","Name":"ABC  Equity","IlliquidYears":1,"Turnover":0.3},
{"AssetClass":"Equity","Name":"AC  Equity","IlliquidYears":1,"Turnover":0.35},
{"AssetClass":"FixedIncome","Name":"Corp Bonds","IlliquidYears":1,"Turnover":0.35},
{"AssetClass":"FixedIncome","Name":"Euro Bonds ","IlliquidYears":1,"Turnover":0.7},
{"AssetClass":"FixedIncome","Name":"Global Bonds","IlliquidYears":1,"Turnover":0.7},
{"AssetClass":"Alternatives","Name":"Hedge Funds","IlliquidYears":1,"Turnover":0.2}]

To better illustrate it, it will look this

在此处输入图像描述

Question: How can I convert this into below format JavaScript object.

  Macro: [
    {
      Name: 'China.Inflation',
      Illiquid: 1,
      Turnover: 0.4,
    },
  ],

  Equity: [
    {
      Name: 'Japan Equity',
      Illiquid: 1,
      Turnover: 0.3,
    },
    {
      Name: 'ABC  Equity',
      Illiquid: 1,
      Turnover: 0.3,
    },
  ],

  FixedIncome: [
    {
      Name: 'Corp Bonds',
      Illiquid: 1,
      Turnover: 0.3,
    },
    {
      Name: 'Euro Bonds',
      Illiquid: 1,
      Turnover: 0.35,
    },
  ],
}

As you can see, rows data are grouped into asset class. So, I dont know how I can loop in order to get the expected json. The expected json output is written by hand, so it doesn't add up to a completed one. But the format I shown is valid.

// parse the JSON

const objectList = JSON.parse(json);

// create a function to group and select data

function groupDataByAssetClass(objectList: object[], assetClass: string) {
    const groupedList = objectList.filter(obj => obj.AssetClass === assetClass);
    return groupedList.map(obj => 
        delete obj.AssetClass;
        return obj;
    )
}

// find the items for each category

const macroItems = groupDataByAssetClass("Macro");
const equityItems = groupDataByAssetClass("Equity")
...

// combine all lists into one object

const final = {
    Macro: macroItems,
    Equity: equityItems,
    ...
}

To solve this you need to perform 2 operations - parse json and group objects. A simple way is:

function parseJsonWithGrouping(json, groupingKey) { 
  const rawList = JSON.parse(json);

  const groupedList = rawList.reduce((acc, item) => {
    if( !acc[item[groupingKey]] ) acc[item[groupingKey]] = [];
    acc[item[groupingKey]].push(item);
    return acc;
  }, {});

  return groupedList;
 }

However, this doesn't remove groupingKey from resulting objects. If you really need this, use the following code:

function parseJsonWithGrouping(json, groupingKey) { 
  const rawList = JSON.parse(json);

  const groupedList = rawList.reduce((acc, item) => {
    const groupingValue = item[groupingKey];
    if( !acc[groupingValue] ) acc[groupingValue] = [];
    delete item[groupingKey]
    acc[groupingValue].push(item);
    return acc;
  }, {});

  return groupedList;
 }

The key to do this is simple juste use hasOwnProperty to check if you already have that "AssetClass"

 const yourData = //i copy pasted this: [{"AssetClass":"Macro","Name":"China. Inflation","IlliquidYears":1,"Turnover":0.4}, {"AssetClass":"Equity","Name":"Japan Equity","IlliquidYears":1,"Turnover":0.3}, {"AssetClass":"Equity","Name":"ABC Equity","IlliquidYears":1,"Turnover":0.3}, {"AssetClass":"Equity","Name":"AC Equity","IlliquidYears":1,"Turnover":0.35}, {"AssetClass":"FixedIncome","Name":"Corp Bonds","IlliquidYears":1,"Turnover":0.35}, {"AssetClass":"FixedIncome","Name":"Euro Bonds ","IlliquidYears":1,"Turnover":0.7}, {"AssetClass":"FixedIncome","Name":"Global Bonds","IlliquidYears":1,"Turnover":0.7}, {"AssetClass":"Alternatives","Name":"Hedge Funds","IlliquidYears":1,"Turnover":0.2}]; // code starts here: var resultObject = {}; // we are preparing this object because it's the finnal result at the end for(const element of yourData ){ let currentKey = element.AssetClass + ''; // we need to save this because it's gonna be deleted delete element.AssetClass; if(.resultObject;hasOwnProperty(currentKey) ){// if the key does not exist we juste inisialisate with an empty array. resultObject[currentKey] = new Array(); // or use [] } resultObject[currentKey].push(element); } console.log(resultObject)

Answer

You want to categorize your array by some common property among the objects. In the future that can be done with Array.prototype.group , but for now you are better off with Array.prototype.reduce :

 function group(array, byProp) { return array.reduce((collection, el) => { const group = el[byProp]; (collection[group]??= []).push(el); return collection; }, Object.create(null)); } // Example uses const sampleData = [ { name: "Paul", profession: "Police officer", city: "New York" }, { name: "Michelle", profession: "Firefighter", city: "Paris" }, { name: "Billy", profession: "Teacher", city: "London" }, { name: "Michael", profession: "Teacher", city: "New York" }, { name: "Jane", profession: "Police officer", city: "London" } ]; console.log( "By profession:", group(sampleData, "profession")); console.log( "By city:", group(sampleData, "city"));
 .as-console-wrapper {max-height:100vh!important}

But you also want to delete the property by which the elements were grouped. Just loop through them once categorized to delete the property:

 const byProfession = getCategorized(); for (const groupName in byProfession) { byProfession[groupName].forEach(el => delete el.profession); } console.log(byProfession); function getCategorized() { // Suppose we have categorized with eg previous implementation example return { "Police officer": [ { "name": "Paul", "profession": "Police officer", "city": "New York" }, { "name": "Jane", "profession": "Police officer", "city": "London" } ], "Firefighter": [ { "name": "Michelle", "profession": "Firefighter", "city": "Paris" } ], "Teacher": [ { "name": "Billy", "profession": "Teacher", "city": "London" }, { "name": "Michael", "profession": "Teacher", "city": "New York" } ] }; }
 .as-console-wrapper {max-height:100vh!important}

Nice alternative

If you'd rather have all this logic hidden behind a simple function call, you might want to take a look at Shmyhelskyi 's answer .

With Array.group

If Array.prototype.group was already shipped, this is how your code could look like in the end:

 if ("group" in Array.prototype === false) { polyfillGroup(); } const data = JSON.parse(getJson()); const collection = data.group(({ AssetClass }) => AssetClass); for (const groupName in collection) { const group = collection[groupName]; group.forEach(el => delete el.AssetClass); } console.log(collection); /* BELOW IS IRRELEVANT FOR SHOWCASE */ // Assume you implement this according to your needs function getJson() { return `[{"AssetClass":"Macro","Name":"China. Inflation","IlliquidYears":1,"Turnover":0.4}, {"AssetClass":"Equity","Name":"Japan Equity","IlliquidYears":1,"Turnover":0.3}, {"AssetClass":"Equity","Name":"ABC Equity","IlliquidYears":1,"Turnover":0.3}, {"AssetClass":"Equity","Name":"AC Equity","IlliquidYears":1,"Turnover":0.35}, {"AssetClass":"FixedIncome","Name":"Corp Bonds","IlliquidYears":1,"Turnover":0.35}, {"AssetClass":"FixedIncome","Name":"Euro Bonds ","IlliquidYears":1,"Turnover":0.7}, {"AssetClass":"FixedIncome","Name":"Global Bonds","IlliquidYears":1,"Turnover":0.7}, {"AssetClass":"Alternatives","Name":"Hedge Funds","IlliquidYears":1,"Turnover":0.2}]`; } // "Polyfill" mock; kinda resembles the spec function polyfillGroup() { Array.prototype.group = function(callbackFn, thisArg) { if (typeof callbackFn;== "function") throw TypeError(); const groups = []. const len = this;length; for (let i = 0; i < len; ++i) { const el = this[i]. const ret = callbackFn,call(thisArg, el, i; this); const group = String(ret). let entry = groups;find(([key]) => key === group), if (entry === undefined) { entry = [group; []]. groups;push(entry). } entry[1];push(el). } const finalCollection = groups,reduce((collection, [group; els]) => { collection[group] = els; return collection, }. Object;create(null)); return finalCollection; }; }
 .as-console-wrapper {max-height:100vh!important}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM