如何将对象数组转换为一个嵌套对象?

huangapple go评论68阅读模式
英文:

How to turn array of objects into one nested object?

问题

{
  "REPORTING PERIOD": "2022",
  "SIGNATURE DATE": "20211005",
  "HOUSE": {
    "OWNER DATA": {
      "FIRST NAME": "Joe",
      "LAST NAME": "Smith"
    },
    "VALUE HISTORY": {
      "INITAL PRICE": "12345",
      "LAST SALE PRICE": "1231236"
    },
    "ADDRESS": {
      "STREET 1": "5 MAIN TERRACE",
      "CITY": "LONDON"
    }
  },
  "AGENT": {
    "COMPANY DATA": {
      "COMPANY NAME": "The Real Agent, Inc",
      "BUSINESS NUMBER": "0021690080"
    },
    "BUSINESS ADDRESS": {
      "STREET 1": "800 MENLO STREET, SUITE 100",
      "CITY": "MENLO PARK",
      "ZIP": "94025"
    }
  }
}
英文:

I have an array of objects. Each object specifies the name of its parent (if any).

How can I turn this into one single object? I'm having trouble handling 2,3+ levels of nesting.

The output should look like this:

{ 
"REPORTING PERIOD": "2022",
"SIGNATURE DATE:" "20211055",
"HOUSE": 
  { "OWNER DATA":
    {"FIRST NAME": "Joe"},
    {"FIRST NAME": "Smith"}
  },
 {"VALUE HISTORY":
    {"INITAL PRICE": ...},
    {"LAST SALE PRICE": ...}
  },
 {"ADDRESS":
    {"STREET 1": ...},
    {"CITY": ...}
  },
}
"AGENT": 
 { etc. }
}

Input:

const data = [
    {
        "rank": 0,
        "key": "REPORTING PERIOD",
        "value": "2022",
        "parent": ""
    },
    {
        "rank": 0,
        "key": "SIGNATURE DATE",
        "value": "20211005",
        "parent": ""
    },
   
    {
        "rank": 0,
        "key": "HOUSE",
        "value": "",
        "parent": ""
    },
    {
        "rank": 1,
        "key": "OWNER DATA",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "FIRST NAME",
        "value": "Joe",
        "parent": "OWNER DATA"
    },
    {
        "rank": 2,
        "key": "LAST NAME",
        "value": "Smith",
        "parent": "OWNER DATA"
    },
    {
        "rank": 1,
        "key": "VALUE HISTORY",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "INITAL PRICE",
        "value": "12345",
        "parent": "VALUE HISTORY"
    },
    {
        "rank": 2,
        "key": "LAST SALE PRICE",
        "value": "1231236",
        "parent": "VALUE HISTORY"
    },
    {
        "rank": 1,
        "key": "ADDRESS",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "STREET 1",
        "value": "5 MAIN TERRACE",
        "parent": "ADDRESS"
    },
    {
        "rank": 2,
        "key": "CITY",
        "value": "LONDON",
        "parent": "ADDRESS"
    },   
    {
        "rank": 0,
        "key": "AGENT",
        "value": "",
        "parent": ""
    },
    {
        "rank": 1,
        "key": "COMPANY DATA",
        "value": "",
        "parent": "AGENT"
    },
    {
        "rank": 2,
        "key": "COMPANY NAME",
        "value": "The Real Agent, Inc",
        "parent": "COMPANY DATA"
    },
    {
        "rank": 2,
        "key": "BUSINESS NUMBER",
        "value": "0021690080",
        "parent": "COMPANY DATA"
    },
    
    {
        "rank": 1,
        "key": "BUSINESS ADDRESS",
        "value": "",
        "parent": "AGENT"
    },
    {
        "rank": 2,
        "key": "STREET 1",
        "value": "800 MENLO STREET, SUITE 100",
        "parent": "BUSINESS ADDRESS"
    },
    {
        "rank": 2,
        "key": "CITY",
        "value": "MENLO PARK",
        "parent": "BUSINESS ADDRESS"
    },
    {
        "rank": 2,
        "key": "ZIP",
        "value": "94025",
        "parent": "BUSINESS ADDRESS"
    }  
]

I can get everything into one object but the nesting doesn't work properly...Playground here

const resultObject: Record<string, unknown> = {};

//loop through array
for (const obj of data) {
    const { key, value, parent } = obj;

    if (parent === " " || parent === "" || parent === undefined) {
      resultObject[key] = value; // If parent is not specified, add it as a direct property in the result object
    } else {
        
      // If parent is specified, nest the element under its parent in the result object
      if (!resultObject[parent]) {
        resultObject[parent] = {}; // Create an empty object for the parent if it doesn't exist
      }
      (resultObject[parent] as Record<string, unknown>)[key] = value;
    }
  }
  console.log(resultObject);

答案1

得分: 1

我们可以结合使用reduce和递归。首先,从排名为0的没有父级的元素开始。然后,使用reduce构建一个对象。如果数据数组中的对象没有值,那么它将成为一个子对象的父级,我们使用递归构建子对象。我们指定子对象必须在当前排名的基础上增加一级,并且它的父级应该是当前正在设置的对象属性的键。

请注意,这种方法允许数据条目无序,而不需要要求父级首先出现在数组中。它还尊重指定的排名,这意味着相同的键可以出现在多个排名级别上,而不会导致代码出错。

const data = [
  {"rank":0,"key":"报告期","value":"2022","parent":""},
  {"rank":0,"key":"签名日期","value":"20211005","parent":""},
  {"rank":0,"key":"房屋","value":"","parent":""},
  {"rank":1,"key":"业主数据","value":"","parent":"房屋"},
  {"rank":2,"key":"名字","value":"Joe","parent":"业主数据"},
  {"rank":2,"key":"姓","value":"Smith","parent":"业主数据"},
  {"rank":1,"key":"价值历史","value":"","parent":"房屋"},
  {"rank":2,"key":"初始价格","value":"12345","parent":"价值历史"},
  {"rank":2,"key":"上次销售价格","value":"1231236","parent":"价值历史"},
  {"rank":1,"key":"地址","value":"","parent":"房屋"},
  {"rank":2,"key":"街道1","value":"5 MAIN TERRACE","parent":"地址"},
  {"rank":2,"key":"城市","value":"伦敦","parent":"地址"},
  {"rank":0,"key":"代理","value":"","parent":""},
  {"rank":1,"key":"公司数据","value":"","parent":"代理"},
  {"rank":2,"key":"公司名称","value":"The Real Agent, Inc","parent":"公司数据"},
  {"rank":2,"key":"营业执照号码","value":"0021690080","parent":"公司数据"},
  {"rank":1,"key":"公司地址","value":"","parent":"代理"},
  {"rank":2,"key":"街道1","value":"800 MENLO STREET, SUITE 100","parent":"公司地址"},
  {"rank":2,"key":"城市","value":"门洛帕克","parent":"公司地址"},
  {"rank":2,"key":"邮编","value":"94025","parent":"公司地址"}
];

const f = (d, p='', r=0) => 
  d.filter(i => i.parent===p && i.rank===r)
  .reduce((a,c) => (a[c.key] = c.value || f(d, c.key, r+1), a), {})

console.log(f(data))
英文:

We can combine reduce and recursion. First, start with elements with no parent at rank 0. Then, build an object using reduce. If an object from the data array has no value, then it will be the parent of a child object, which we build using recursion. We specify that the child must be at a rank one greater than the current rank, and that it should have as a parent the key of the current object property being set.

Note that this approach allows the data entries to be out of order, instead of requiring the parents to appear first in the array. It also respects the specified ranks, meaning that the same key can occur at multiple rank levels, without the code breaking.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const data = [{&quot;rank&quot;:0,&quot;key&quot;:&quot;REPORTING PERIOD&quot;,&quot;value&quot;:&quot;2022&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;SIGNATURE DATE&quot;,&quot;value&quot;:&quot;20211005&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;HOUSE&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;OWNER DATA&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;FIRST NAME&quot;,&quot;value&quot;:&quot;Joe&quot;,&quot;parent&quot;:&quot;OWNER DATA&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;LAST NAME&quot;,&quot;value&quot;:&quot;Smith&quot;,&quot;parent&quot;:&quot;OWNER DATA&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;VALUE HISTORY&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;INITAL PRICE&quot;,&quot;value&quot;:&quot;12345&quot;,&quot;parent&quot;:&quot;VALUE HISTORY&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;LAST SALE PRICE&quot;,&quot;value&quot;:&quot;1231236&quot;,&quot;parent&quot;:&quot;VALUE HISTORY&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;ADDRESS&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;STREET 1&quot;,&quot;value&quot;:&quot;5 MAIN TERRACE&quot;,&quot;parent&quot;:&quot;ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;CITY&quot;,&quot;value&quot;:&quot;LONDON&quot;,&quot;parent&quot;:&quot;ADDRESS&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;AGENT&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;COMPANY DATA&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;AGENT&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;COMPANY NAME&quot;,&quot;value&quot;:&quot;The Real Agent, Inc&quot;,&quot;parent&quot;:&quot;COMPANY DATA&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;BUSINESS NUMBER&quot;,&quot;value&quot;:&quot;0021690080&quot;,&quot;parent&quot;:&quot;COMPANY DATA&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;BUSINESS ADDRESS&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;AGENT&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;STREET 1&quot;,&quot;value&quot;:&quot;800 MENLO STREET, SUITE 100&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;CITY&quot;,&quot;value&quot;:&quot;MENLO PARK&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;ZIP&quot;,&quot;value&quot;:&quot;94025&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;}]
const f = (d, p=&#39;&#39;, r=0) =&gt; 
d.filter(i =&gt; i.parent===p &amp;&amp; i.rank===r)
.reduce((a,c) =&gt; (a[c.key] = c.value || f(d, c.key, r+1), a), {})
console.log(f(data))  

<!-- end snippet -->

答案2

得分: 1

你可以通过一次 reduce() 调用来实现所需的树结构,同时使用相同的对象进行累积和查找。通过访问返回对象上的适当顶级 parent 值来检索结果。这里使用了 nullish coalescing assignment (??=) 来检索/分配每个级别。

const data = [
  {"rank":0,"key":"REPORTING PERIOD","value":"2022","parent":""},
  {"rank":0,"key":"SIGNATURE DATE","value":"20211005","parent":""},
  {"rank":0,"key":"HOUSE","value":"","parent":""},
  {"rank":1,"key":"OWNER DATA","value":"","parent":"HOUSE"},
  {"rank":2,"key":"FIRST NAME","value":"Joe","parent":"OWNER DATA"},
  {"rank":2,"key":"LAST NAME","value":"Smith","parent":"OWNER DATA"},
  {"rank":1,"key":"VALUE HISTORY","value":"","parent":"HOUSE"},
  {"rank":2,"key":"INITIAL PRICE","value":"12345","parent":"VALUE HISTORY"},
  {"rank":2,"key":"LAST SALE PRICE","value":"1231236","parent":"VALUE HISTORY"},
  {"rank":1,"key":"ADDRESS","value":"","parent":"HOUSE"},
  {"rank":2,"key":"STREET 1","value":"5 MAIN TERRACE","parent":"ADDRESS"},
  {"rank":2,"key":"CITY","value":"LONDON","parent":"ADDRESS"},
  {"rank":0,"key":"AGENT","value":"","parent":""},
  {"rank":1,"key":"COMPANY DATA","value":"","parent":"AGENT"},
  {"rank":2,"key":"COMPANY NAME","value":"The Real Agent, Inc","parent":"COMPANY DATA"},
  {"rank":2,"key":"BUSINESS NUMBER","value":"0021690080","parent":"COMPANY DATA"},
  {"rank":1,"key":"BUSINESS ADDRESS","value":"","parent":"AGENT"},
  {"rank":2,"key":"STREET 1","value":"800 MENLO STREET, SUITE 100","parent":"BUSINESS ADDRESS"},
  {"rank":2,"key":"CITY","value":"MENLO PARK","parent":"BUSINESS ADDRESS"},
  {"rank":2,"key":"ZIP","value":"94025","parent":"BUSINESS ADDRESS"}
];

const tree = data.reduce((a, { parent, key, value }) => {
  (a[parent] ??= {})[key] = value === '' ? (a[key] ??= {}) : value;
  return a;
}, {})[''];

console.log(tree);
英文:

You can achieve your desired tree in a single pass of a reduce() call using the same object for both accumulation and lookup. The result is retrieved by accessing the appropriate top level parent value on the returned object. Here using nullish coalescing assignment (??=) to retrieve/assign each level.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const data = [{&quot;rank&quot;:0,&quot;key&quot;:&quot;REPORTING PERIOD&quot;,&quot;value&quot;:&quot;2022&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;SIGNATURE DATE&quot;,&quot;value&quot;:&quot;20211005&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;HOUSE&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;OWNER DATA&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;FIRST NAME&quot;,&quot;value&quot;:&quot;Joe&quot;,&quot;parent&quot;:&quot;OWNER DATA&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;LAST NAME&quot;,&quot;value&quot;:&quot;Smith&quot;,&quot;parent&quot;:&quot;OWNER DATA&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;VALUE HISTORY&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;INITAL PRICE&quot;,&quot;value&quot;:&quot;12345&quot;,&quot;parent&quot;:&quot;VALUE HISTORY&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;LAST SALE PRICE&quot;,&quot;value&quot;:&quot;1231236&quot;,&quot;parent&quot;:&quot;VALUE HISTORY&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;ADDRESS&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;HOUSE&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;STREET 1&quot;,&quot;value&quot;:&quot;5 MAIN TERRACE&quot;,&quot;parent&quot;:&quot;ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;CITY&quot;,&quot;value&quot;:&quot;LONDON&quot;,&quot;parent&quot;:&quot;ADDRESS&quot;},{&quot;rank&quot;:0,&quot;key&quot;:&quot;AGENT&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;COMPANY DATA&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;AGENT&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;COMPANY NAME&quot;,&quot;value&quot;:&quot;The Real Agent, Inc&quot;,&quot;parent&quot;:&quot;COMPANY DATA&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;BUSINESS NUMBER&quot;,&quot;value&quot;:&quot;0021690080&quot;,&quot;parent&quot;:&quot;COMPANY DATA&quot;},{&quot;rank&quot;:1,&quot;key&quot;:&quot;BUSINESS ADDRESS&quot;,&quot;value&quot;:&quot;&quot;,&quot;parent&quot;:&quot;AGENT&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;STREET 1&quot;,&quot;value&quot;:&quot;800 MENLO STREET, SUITE 100&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;CITY&quot;,&quot;value&quot;:&quot;MENLO PARK&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;},{&quot;rank&quot;:2,&quot;key&quot;:&quot;ZIP&quot;,&quot;value&quot;:&quot;94025&quot;,&quot;parent&quot;:&quot;BUSINESS ADDRESS&quot;}]
const tree = data.reduce((a, { parent, key, value }) =&gt; {
(a[parent] ??= {})[key] = value === &#39;&#39; ? (a[key] ??= {}) : value;
return a;
}, {})[&#39;&#39;];
console.log(tree);

<!-- end snippet -->

答案3

得分: 0

以下是翻译好的代码部分:

可以使用另一个查找对象来存储对每个对象的引用,从而实现无需嵌套循环即可进行简单修改。

const res: Record<string, unknown> = {};
const lookup: typeof res = {};
for (const {key, value, parent} of data)
    ((parent ? lookup[parent] : res) as typeof res)[key] = lookup[key] = value || {};
console.log(res);

如果数据可以以任何顺序出现,并且除了空字符串以外可能存在其他假值,那么您可以使用以下方法:

for (const {key, value, parent} of data)
    ((parent ? lookup[parent] ??= {} : res) as typeof res)[key] = 
        lookup[key] ??= value !== '' ? value : {};
英文:

You can use another lookup object to store references to each object, enabling easy modification with no nested loops.

const res: Record&lt;string, unknown&gt; = {};
const lookup: typeof res = {};
for (const {key, value, parent} of data)
    ((parent ? lookup[parent] : res) as typeof res)[key] = lookup[key] = value || {};
console.log(res);

If the data can be in any order and other falsy values apart from the empty string might be present, then you may use this approach:

for (const {key, value, parent} of data)
    ((parent ? lookup[parent] ??= {} : res) as typeof res)[key] = 
        lookup[key] ??= value !== &#39;&#39; ? value : {};

huangapple
  • 本文由 发表于 2023年6月2日 08:25:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386483.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定