skip to Main Content

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);

3

Answers


  1. 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.

    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"}]
    
    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))  
    Login or Signup to reply.
  2. You can use another lookup object to store references to each object, enabling easy modification with no nested loops.

    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);
    

    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 !== '' ? value : {};
    
    Login or Signup to reply.
  3. 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.

    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"}]
    
    const tree = data.reduce((a, { parent, key, value }) => {
      (a[parent] ??= {})[key] = value === '' ? (a[key] ??= {}) : value;
    
      return a;
    }, {})[''];
    
    console.log(tree);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search