-1

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.

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Smit Gabani
  • 240
  • 2
  • 6

5 Answers5

1

"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; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
0

Assuming you have an array of these objects, you can easily combine each object with its "address" property using destructuring:

const myInput = [
  {
    name: 'john doe',
    address: { apartment: 1550, streetno: 167, streetname: 'Victoria'}
  },
  {
    name: 'Joe Smith',
    address: { apartment: 2, streetno: 111, streetname: 'Jones'}
  }
];

const myOutput = myInput.map(({address, ...rest}) => ({...rest, ...address}));
console.log(myOutput);
James
  • 20,957
  • 5
  • 26
  • 41
0

map over the array and return a new object that has had its address property merged into it, the address property deleted, and the new object returned.

const arr=[{name:"john doe",address:{apartment:1550,streetno:167,streetname:"Victoria",a:"a"},b:"b"}];

const out = arr.map(obj => {
  const newObj = { ...obj, ...obj.address };
  delete newObj.address;
  return newObj;
});

console.log(out);
Andy
  • 61,948
  • 13
  • 68
  • 95
  • can you check the updated question – Smit Gabani May 28 '22 at 08:36
  • 1
    You'll need to use some form of recursion for that. Also you would need to ensure that none of the property names are duplicates of any other property name in the nested object or they will be overwritten. – Andy May 28 '22 at 08:44
0

Here's a fairly simply approach based on a recursive function that first converts an element to the following form and then calls Object .fromEntries on the result:

[
  ["name", "John Doe"],
  ["apartment", 1550],
  ["streetno", 167],
  ["streetname", "Victoria"]
]

It looks like this:

const deepEntries = (o) =>
  Object .entries (o) .flatMap (([k, v]) => v.constructor === Object ? deepEntries (v) : [[k, v]])

const deepFlat = (o) =>
  Object .fromEntries (deepEntries (o))

const deepFlatAll = (xs) =>
  xs .map (deepFlat)

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

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

But you do have a possible significant concern here. If you're flattening multiple levels, it's quite possible that the different levels have nodes with the same name; most of those will be clobbered when you put the object back together.

One solution to such a problem would be to flatten to a different format. I've seen something like this used quite successfully:

{
  "name": "John Doe",
  "address.apartment": 1550,
  "address.streetno": 167,
  "address.streetname": "Victoria"
}

If you look around StackOverflow, you can certainly find answers for how to do that.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
-1

That isn't an array.

If you want to flatten a dictionary, do it this way: https://stackoverflow.com/a/22047469/5676613

This has O(n^k) and Omega(n) time complexity where n is the size of the dictionary, and k is the depth of the dictionary (or how many nests are in the dictionary).