157

Is there a built-in lodash function to take this:

var params = [
    { name: 'foo', input: 'bar' },
    { name: 'baz', input: 'zle' }
];

And output this:

var output = {
    foo: 'bar',
    baz: 'zle'
};

Right now I'm just using Array.prototype.reduce():

function toHash(array, keyName, valueName) {
    return array.reduce(function(dictionary, next) {
        dictionary[next[keyName]] = next[valueName];
        return dictionary;
    }, {});
}

toHash(params, 'name', 'input');

Wondering if there's a lodash short-cut.

8 Answers 8

211

Another way with lodash 4.17.2

_.chain(params)
    .keyBy('name')
    .mapValues('input')
    .value();

or

_.mapValues(_.keyBy(params, 'name'), 'input')

or with _.reduce

_.reduce(
    params,
    (acc, { name, input }) => ({ ...acc, [name]: input }),
    {}
)
Sign up to request clarification or add additional context in comments.

5 Comments

@Akrikos the _.keyBy will transform the entire array to an object, whereas the question is mostly about having one item from each object in array as key and one other item as its value. If _.keyBy is used, then all values will be objects.
Thanks @MustafaEhsanAlokozay You're right, that answer does not do the exact right thing. I'll delete my comment as it's not helpful. That may make your comment look weird, but better that than have my incorrect comment stay up any longer.
Anyone knows how to make this work with Typescript? is there a way to get object of output type with lodash?
For reduce, just use es6, zero point in lodash for this case. For Typescript, just put in the resulting type balances.reduce<Record<string, AccountBalanceValue>>
@vilemcech You'll have to cast the accumulator dictionary to get Typescript to be happy. For this example, it would be: {} as { [key: string]: string }.
97

You should be using _.keyBy to easily convert an array to an object.

Docs here

Example usage below:

var params = [
    { name: 'foo', input: 'bar' },
    { name: 'baz', input: 'zle' }
];
console.log(_.keyBy(params, 'name'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

If required, you can manipulate the array before using _.keyBy or the object after using _.keyBy to get the exact desired result.

4 Comments

Look first at this to understand most voted answer by stasovlas.
This is a cool answer, but it does NOT answer the question. The values should be strings not objects.
is there a way to have it not convert key into string?
This does not answer the question since it does not return what is asked, namely: { foo: 'baz', baz: 'zle' }
44

Yep it is here, using _.reduce

var params = [
    { name: 'foo', input: 'bar' },
    { name: 'baz', input: 'zle' }
];

_.reduce(params , function(obj,param) {
 obj[param.name] = param.input
 return obj;
}, {});

3 Comments

Yet again reduce() swoops in with another use case. Smart way of doing it!
Anyone got the typescripted version of this?
The reality is that's mathematically true that you can implement literally any iteration action using reduce/inject, since reduce is isomorphic to a list. However, that flexibility comes with a price in readability. Don't use _.reduce unless that accurately expresses what you're trying to do: it significantly obscures the point of the code in any other case. I personally prefer the _.zipObject solution by @djechlin -- failing that, the _.keyBy/_.mapValues approach.
26

This seems like a job for Object.assign:

const output = Object.assign({}, ...params.map(p => ({[p.name]: p.input})));

Edited to wrap as a function similar to OP's, this would be:

const toHash = (array, keyName, valueName) => 
    Object.assign({}, ...array.map(o => ({[o[keyName]]: o[valueName]})));

(Thanks to Ben Steward, good thinking...)

2 Comments

Maybe wrap that in a parent function so the user can determine what keys would be used by passing them, like the OP had.
Thank you. This works well also if you want the value to be something else than input. For example, a boolean
16

This is probably more verbose than you want, but you're asking for a slightly complex operation so actual code might be involved (the horror).

My recommendation, with zipObject that's pretty logical:

_.zipObject(_.map(params, 'name'), _.map(params, 'input'));

Another option, more hacky, using fromPairs:

_.fromPairs(_.map(params, function(val) { return [val['name'], val['input']));

The anonymous function shows the hackiness -- I don't believe JS guarantees order of elements in object iteration, so callling .values() won't do.

1 Comment

I don't see anything hacky about using fromPairs, and calling values() indeed won't do. At least from readability standpoint.
12

You can use one liner javascript with array reduce method and ES6 destructuring to convert array of key value pairs to object.

arr.reduce((map, { name, input }) => ({ ...map, [name]: input }), {});

3 Comments

Unfortunately, this is O(n^2).
arr.reduce((map, { name, input }) => (map[name] = input, map), {}); @jimrandomh what about this?
That version is O(n), but it modifies its input, which is something you usually don't want. I think for this sort of thing, it's generally better to write a full-length utility function with a foreach loop, put it in a utils folder out of the way somewhere and import it, rather than trying to be clever with one-liners.
8

Another way with lodash

creating pairs, and then either construct a object or ES6 Map easily

_(params).map(v=>[v.name, v.input]).fromPairs().value()

or

_.fromPairs(params.map(v=>[v.name, v.input]))

Here is a working example

var params = [
    { name: 'foo', input: 'bar' },
    { name: 'baz', input: 'zle' }
];

var obj = _(params).map(v=>[v.name, v.input]).fromPairs().value();

console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

2 Comments

The good part of this solution is that it's also feasible in plain ES: Object.fromEntries(params.map(v => [v.name, v.input]))
@fregante, Chrome 73+ only caniuse.com/?search=fromEntries
7

It can also solve without using any lodash function like this:

let paramsVal = [
    { name: 'foo', input: 'bar' },
    { name: 'baz', input: 'zle' }
];
let output = {};

paramsVal.forEach(({name, input})=>{
  output[name] = input;
})


console.log(output);

enter image description here

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.