1

I’m looking for a compact lodash solution to take an array of objects and then create a new object with the keys of the objects in the array and the unique values for each key.

[
    {
        color: "black",
        type: "bag",
    },
    {
        color: "red",
        type: "pants",
    },
    {
        color: "black",
        type: "jacket",
    },
]

Desired output:

{
    color: ["black", "red"],
    type: ["bag", "pants", "jacket"],
}

Thanks!

3 Answers 3

3

You can solve this using lodash#flatMap with a lodash#toPairs iteratee, to create a flattened version of an array pair of keys and values. Use lodash#groupBy with an iteratee function that removes the key of the array pairs, Array#shift, and use such value as the grouping key for the collection. Lastly, use lodash#mapValues with an iteratee that is composed of lodash#flatten and lodash#uniq, to flatten the array and only get the unique values for each grouped array. Note that the function composition uses lodash#flow to compose successive function arguments to form one functional result.

var result = _(array)
  .flatMap(_.toPairs)
  .groupBy(v => v.shift())
  .mapValues(_.flow(_.flatten, _.uniq))
  .value();

var array = [
    {
        color: "black",
        type: "bag",
    },
    {
        color: "red",
        type: "pants",
    },
    {
        color: "black",
        type: "jacket",
    }
];

var result = _(array)
  .flatMap(_.toPairs)
  .groupBy(v => v.shift())
  .mapValues(_.flow(_.flatten, _.uniq))
  .value();
  
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

1

I can't see a function that'll do this for you, so here's a solution that solves the problem in a number of straightforward steps using only lodash functions.

const listOfPairs = _.flatten(_.map(input, _.toPairs))

transforms the list of objects into a list of pairs of [key, value]

listOfPairs = [
  [ 'color', 'black' ],
  [ 'type', 'bag' ],
  [ 'color', 'red' ],
  [ 'type', 'pants' ] ]

now, we can group these up by the values in the first position in each pair.

const indexByKeyToValues = _.toPairs(_.groupBy(listOfPairs, _.head))

which gives us

indexByKeyToValues = [
  [ 'color', [ ['color', 'black'], ['color', 'red'] ] ],
  [ 'type', [ ['type', 'bag'], ['type', 'pants'] ] ] ]

then, map over the value arrays to pick the last element (the original values in the input maps)

const pairsOfKeyAndValue = _.map(indexByKeyToValues, ([k, vs]) => [k, _.map(vs, _.last)])

which is almost there

pairsOfKeyAndValue = [
  [ 'color', [ 'black', 'red' ] ],
  [ 'type', [ 'bag', 'pants' ] ] ]

we just need to rebuild an object using these pairs

const result = _.fromPairs(pairsOfKeyAndValue)

The whole "transforming a map to and from sequences of pairs" trick is really common in functional programming to do this kind of processing. Once you've done that, figuring out the rest of the steps isn't too tricky.

Hopefully this gives you a general idea you can use to solve these kinds of problems in future.

Comments

1

Use what's built-in. It's faster, shorter, and easier to reason from.

const arr = [{color: "black",type: "bag",},{color: "red",type: "pants",},{color: "black",type:"jacket",}],
obj = arr.reduce((h, y) => {
  Object.keys(y).forEach(k => {
    if (!h[k]) {
      h[k] = []
      h[k].push(y[k])
    } else if (!h[k].includes(y[k])) h[k].push(y[k])
  })
  return h
}, {})
console.log(obj)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.