jchi2241
jchi2241

Reputation: 2226

Flatten nested object with lodash or es6 javascript

I have an object that originally looks like this

{
  foo: {
    fruit: "watermelon",
    vege: "celery",
  },
  bar: {
    fruit: "banana",
    candy: "snickers",
    drink: "coke",
  },
  ...
}

but want to "flatten" and have it look like this

{
  fruit: "banana",
  vege: "celery",
  candy: "snickers",
  drink: "coke",
}

If there's an overlap in the sub-object keys, just overwrite the previous one.

What's a sane way to do this?

Upvotes: 0

Views: 1459

Answers (5)

Ori Drori
Ori Drori

Reputation: 191986

With lodash you can use _.values() that generates an array, and then spread to _.merge(). I'm using _.flow() to generate a function that does that:

const fn = _.flow(_.values, _.spread(_.merge))

const obj = {
  foo: {
    fruit: "watermelon",
    vege: "celery",
  },
  bar: {
    fruit: "banana",
    candy: "snickers",
    drink: "coke",
  },
}

const result = fn(obj)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386654

You could take two function for getting a flat object from multi nested objects.

The outer gets from an array of objects a flat object and the recursinve function returns a flat array with object for every level.

const
    flat = data => Object.assign({}, ...pairs(data)),
    pairs = o => o && typeof o === 'object' && Object
        .entries(o)
        .flatMap(([k, v]) => pairs(v) || { [k]: v }),
    data = { 
        foo: {
            fruit: "watermelon",
            vege: "celery"
        },
        bar: {
            fruit: "banana",
            candy: "snickers",
            drink: "coke"
        },
        n: {
            m: {
                o: {
                    p: 42
                }
            }
        }
    },
    result = flat(data);

console.log(result);

Upvotes: 0

Nick Parsons
Nick Parsons

Reputation: 50759

You can grab the values of your object using Object.values(), and then use Object.assign() with the spread syntax to merge all the objects together:

const obj = { foo: { fruit: "watermelon", vege: "celery", }, bar: { fruit: "banana", candy: "snickers", drink: "coke", } };

const res = Object.assign({}, ...Object.values(obj));
console.log(res);

With lodash you can use the corresponding methods _.assign() and _.values():

const obj = { foo: { fruit: "watermelon", vege: "celery", }, bar: { fruit: "banana", candy: "snickers", drink: "coke", } };

const res = _.assign({}, ..._.values(obj));
console.log(res);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Both of these methods rely on the iteration order that Object.values() and for...in uses. In the latest spec this is something which can be relied on, but this hasn't always been the case. If you need to read the values in a guaranteed order 100% of the time, you can consider specifying the order of keys using an array:

const obj = { foo: { fruit: "watermelon", vege: "celery", }, bar: { fruit: "banana", candy: "snickers", drink: "coke", } };

const order = ["foo", "bar"]; // visit foo first then bar
const res = Object.assign({}, ...order.map(k => obj[k]));
console.log(res);

Upvotes: 4

wangdev87
wangdev87

Reputation: 8751

const input = {
  foo: {
    fruit: "watermelon",
    vege: "celery",
  },
  bar: {
    fruit: "banana",
    candy: "snickers",
    drink: "coke",
  },
}

var result = {}
Object.values(input).forEach(item => {
  result = {
    ...result,
    ...item
  }
});
console.log(result)

Upvotes: 0

Derek Wang
Derek Wang

Reputation: 10194

You can get values of object using Object.prototype.values and using Array.prototype.forEach, you can get the merge the object [key, value] pairs as follows.

const input = {
  foo: {
    fruit: "watermelon",
    vege: "celery",
  },
  bar: {
    fruit: "banana",
    candy: "snickers",
    drink: "coke",
  }
};

let output = {};
Object.values(input).forEach((item) => {
  output = {
    ...output,
    ...item
  };
});
console.log(output);

Upvotes: 0

Related Questions