0

With the following data format:

const flat = {
    '100': 'L1',
    '100.200': 'L1, J2',
    '100.200.300': 'L1, J2, A3',
    '100.200.400': 'L1, J2, A4',
    '100.300': 'L1, J3',
    '100.400.500': 'L1, J4, A5'
};

I want to change it to something like this:

{
    "100":{
        "name":"L1",
        "children":{
            "200":{
                "name":"L1, J2",
                "children":{
                    "300":{
                        "name":"L1, J2, A3"
                    },
                    "400":{
                        "name":"L1, J2, A4"
                    }
                }
            },
            "300":{
                "name":"L1, J3"
            },
            "400":{
                "name":null,
                "children":{
                    "500":{
                        "name":"L1, J4, A5"
                    }
                }
            }
        }
    }
}

I've been trying to implement some things from this Stack Overflow post although they only cover a lot of my use cases and it's quite hard to figure out.

halfer
  • 19,824
  • 17
  • 99
  • 186
bryan
  • 8,879
  • 18
  • 83
  • 166
  • 1
    `Object.keys` to get all the keys from the flat object, `Array.map` with `String.split` to get parts of the object keys and `Array.reduce` to return the object you want – Roberto Zvjerković May 11 '21 at 21:27
  • 1
    "*I've been trying to implement some things*" - please [edit] your question to include those attempts, otherwise we can't tell you what went wrong and will only be able to point you back to those existing questions about the topic. – Bergi May 11 '21 at 22:47

1 Answers1

1

Here's an example using an Array.prototype.reduce() call on the Object.entries() of the supplied object.

function expand(obj) {
  return Object
    .entries(obj)
    .reduce((a, [propString, name]) => {
      const
        propArr = propString.split('.'),
        innerProp = propArr.pop(),
        innerObj = propArr.reduce((_a, prop) => (
          _a[prop] ??= {}, _a[prop]), a);

      innerObj[innerProp] = { name };

      return a;
    }, {})
}

const flat = { '100': 'L1', '100.200': 'L1, J2', '100.200.300': 'L1, J2, A3', '100.200.400': 'L1, J2, A4', '100.300': 'L1, J3', '100.400.500': 'L1, J4, A5' };

console.log(expand(flat));
.as-console-wrapper { max-height: 100% !important; top: 0; }

But the question you linked, Convert javascript dot notation object to nested object, works just fine if you make a single change to accommodate the shape of your input.

target[parts[0]] = obj[objectPath]

becomes

target[parts[0]] = { name: obj[objectPath] }

// https://stackoverflow.com/questions/7793811/convert-javascript-dot-notation-object-to-nested-object

function deepen(obj) {
  const result = {};

  // For each object path (property key) in the object
  for (const objectPath in obj) {
    // Split path into component parts
    const parts = objectPath.split('.');

    // Create sub-objects along path as needed
    let target = result;
    while (parts.length > 1) {
      const part = parts.shift();
      target = target[part] = target[part] || {};
    }

    // Set value at end of path
    target[parts[0]] = { name: obj[objectPath] }
  }

  return result;
}


const flat = { '100': 'L1', '100.200': 'L1, J2', '100.200.300': 'L1, J2, A3', '100.200.400': 'L1, J2, A4', '100.300': 'L1, J3', '100.400.500': 'L1, J4, A5' };

console.log(deepen(flat));
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • Thank you! This makes total sense. Your expand function ended up failing when I put a super large dataset in but the second one worked great. – bryan May 12 '21 at 18:24