Skip to content
Advertisement

How to entirely level (map / reduce / recursion) a nested object of unknown depth in the most efficient manner?

I would like to do something like the following but in a more large scale and efficient way. Assume I have an array of objects where each object needs to be leveled/flattened.

Convert something like this …

[{
  name: 'John Doe',
  address: {
    apartment: 1550,
    streetno: 167,
    streetname: 'Victoria',
  },
}, {
  name: 'Joe Smith',
  address: {
    apartment: 2,
    streetno: 111,
    streetname: 'Jones',
  },
}]

… to that …

[{
  name: 'John Doe',
  apartment: 1550,
  streetno: 167,
  streetname: 'Victoria',
}, {
  name: 'Joe Smith',
  apartment: 2,
  streetno: 111,
  streetname: 'Jones',
}]

As is shown above, address as well is an object which needs to be leveled/flattened.

But most importantly, one does not know the object/data-structure in advance. Thus one neither knows property-names nor the depth of the nested levels.

Advertisement

Answer

“So before receiving the object you do not know much about its structure.”

The OP’s main task actually is to level any given nested object-based data-structure into an object of just a single entries-level. And because one does not know anything about a data-structure in advance, one has to come up with a recursive approach.

Once implemented, such a function of cause can be used as callback for an array’s mapping process.

The recursive implementation itself is based on type-detection (distinguish in between Array– and Object-types and primitive values) and on reduceing the entries (key-value pairs) of an object according to the currently processed value‘s type.

function recursivelyLevelObjectEntriesOnly(type) {
  let result = type;
  if (Array.isArray(type)) {

    result = type
      .map(recursivelyLevelObjectEntriesOnly);

  } else if (type && 'object' === typeof type) {

    result = Object
      .entries(type)
      .reduce((merger, [key, value]) => {

        if (value && 'object' === typeof value && !Array.isArray(value)) {

          Object.assign(merger, recursivelyLevelObjectEntriesOnly(value));
        } else {
          merger[key] = recursivelyLevelObjectEntriesOnly(value);
        }
        return merger;

      }, {});    
  }
  return result;
}

const sampleData = [{
  name: 'John Doe',
  address: { apartment: 1550, streetno: 167, streetname: 'Victoria' },
}, {
  name: 'Joe Smith',
  address: { apartment: 2, streetno: 111, streetname: 'Jones' },
}, {
  foo: {
    bar: "bar",
    baz: "baz",
    biz: {
      buzz: "buzz",
      bizz: [{
        name: 'John Doe',
        address: { apartment: 1550, streetno: 167, streetname: 'Victoria' },
      }, {
        name: 'Joe Smith',
        address: { apartment: 2, streetno: 111, streetname: 'Jones' },
      }, {
        foo: {
          bar: "bar",
          baz: "baz",
          biz: {
            buzz: "buzz",
            booz: {
              foo: "foo",
            },
          },
        },
      }],
      booz: {
        foo: "foo",
      },
    },
  },
}];

const leveledObjectData = sampleData.map(recursivelyLevelObjectEntriesOnly);
console.log({ leveledObjectData });

// no mutation at `sampleData`.
console.log({ sampleData });
.as-console-wrapper { min-height: 100%!important; top: 0; }
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement