138

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.

core
  • 32,451
  • 45
  • 138
  • 193

8 Answers8

191

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 }),
    {}
)
Mustafa Ehsan Alokozay
  • 5,583
  • 2
  • 24
  • 30
stasovlas
  • 7,136
  • 2
  • 28
  • 29
  • @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. – Mustafa Ehsan Alokozay Oct 19 '19 at 09:40
  • 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. – Akrikos Mar 06 '20 at 19:19
  • Anyone knows how to make this work with Typescript? is there a way to get object of output type with lodash? – vilem cech Apr 12 '23 at 14:35
  • For reduce, just use es6, zero point in lodash for this case. For Typescript, just put in the resulting type `balances.reduce>` – Frexuz Sep 01 '23 at 04:16
85

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.

danday74
  • 52,471
  • 49
  • 232
  • 283
43

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;
}, {});
Jagdish Idhate
  • 7,513
  • 9
  • 35
  • 51
  • 1
    Yet again `reduce()` swoops in with another use case. Smart way of doing it! – Dan Jul 18 '19 at 15:53
  • Anyone got the typescripted version of this? – mr.bjerre Feb 05 '20 at 18:49
  • 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. – Robert Fischer Aug 20 '20 at 14:01
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...)

RainedColony
  • 261
  • 3
  • 4
  • 1
    Maybe wrap that in a parent function so the user can determine what keys would be used by passing them, like the OP had. – Ben Steward Jan 30 '19 at 20:35
  • Thank you. This works well also if you want the value to be something else than input. For example, a boolean – utopia Jan 17 '23 at 19:17
13

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.

djechlin
  • 59,258
  • 35
  • 162
  • 290
  • I don't see anything hacky about using `fromPairs`, and calling `values()` indeed won't do. At least from readability standpoint. – x-yuri Aug 20 '17 at 12:03
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 }), {});
Faisal Hasnain
  • 475
  • 6
  • 8
  • 3
    Unfortunately, this is O(n^2). – jimrandomh Feb 20 '19 at 00:31
  • `arr.reduce((map, { name, input }) => (map[name] = input, map), {});` @jimrandomh what about this? – Mizanur Rahman Mojumder Jun 16 '22 at 07:32
  • 2
    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. – jimrandomh Jun 16 '22 at 08:30
7

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>
Koushik Chatterjee
  • 4,106
  • 3
  • 18
  • 32
  • 1
    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 Jun 03 '20 at 12:55
  • 1
    @fregante, Chrome 73+ only https://caniuse.com/?search=fromEntries – d9k Oct 23 '20 at 09:49
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