[javascript] Convert object array to hash map, indexed by an attribute value of the Object

Use Case

The use case is to convert an array of objects into a hash map based on string or function provided to evaluate and use as the key in the hash map and value as an object itself. A common case of using this is converting an array of objects into a hash map of objects.

Code

The following is a small snippet in JavaScript to convert an array of objects to a hash map, indexed by the attribute value of object. You can provide a function to evaluate the key of hash map dynamically (run time). Hope this helps someone in future.

function isFunction(func) {
    return Object.prototype.toString.call(func) === '[object Function]';
}

/**
 * This function converts an array to hash map
 * @param {String | function} key describes the key to be evaluated in each object to use as key for hashmap
 * @returns Object
 * @Example 
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap("id")
 *      Returns :- Object {123: Object, 345: Object}
 *
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap(function(obj){return obj.id+1})
 *      Returns :- Object {124: Object, 346: Object}
 */
Array.prototype.toHashMap = function(key) {
    var _hashMap = {}, getKey = isFunction(key)?key: function(_obj){return _obj[key];};
    this.forEach(function (obj){
        _hashMap[getKey(obj)] = obj;
    });
    return _hashMap;
};

You can find the gist here: Converts Array of Objects to HashMap.

This question is related to javascript arrays hashmap

The answer is


try

let toHashMap = (a,f) => a.reduce((a,c)=> (a[f(c)]=c,a),{});

_x000D_
_x000D_
let arr=[_x000D_
  {id:123, name:'naveen'}, _x000D_
  {id:345, name:"kumar"}_x000D_
];_x000D_
_x000D_
let fkey = o => o.id; // function changing object to string (key)_x000D_
_x000D_
let toHashMap = (a,f) => a.reduce((a,c)=> (a[f(c)]=c,a),{});_x000D_
_x000D_
console.log( toHashMap(arr,fkey) );_x000D_
_x000D_
// Adding to prototype is NOT recommented:_x000D_
//_x000D_
// Array.prototype.toHashMap = function(f) { return toHashMap(this,f) };_x000D_
// console.log( arr.toHashMap(fkey) );
_x000D_
_x000D_
_x000D_


With lodash:

const items = [
    { key: 'foo', value: 'bar' },
    { key: 'hello', value: 'world' }
];

const map = _.fromPairs(items.map(item => [item.key, item.val]));

// OR: if you want to index the whole item by key:
// const map = _.fromPairs(items.map(item => [item.key, item]));

The lodash fromPairs function reminds me about zip function in Python

Link to lodash


There are better ways to do this as explained by other posters. But if I want to stick to pure JS and ol' fashioned way then here it is:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' },
    { key: 'hello', val: 'universe' }
];

var map = {};
for (var i = 0; i < arr.length; i++) {
    var key = arr[i].key;
    var value = arr[i].val;

    if (key in map) {
        map[key].push(value);
    } else {
        map[key] = [value];
    }
}

console.log(map);

You can use the new Object.fromEntries() method.

Example:

_x000D_
_x000D_
const array = [_x000D_
   {key: 'a', value: 'b', redundant: 'aaa'},_x000D_
   {key: 'x', value: 'y', redundant: 'zzz'}_x000D_
]_x000D_
_x000D_
const hash = Object.fromEntries(_x000D_
   array.map(e => [e.key, e.value])_x000D_
)_x000D_
_x000D_
console.log(hash) // {a: b, x: y}
_x000D_
_x000D_
_x000D_


Following is small snippet i've created in javascript to convert array of objects to hash map, indexed by attribute value of object. You can provide a function to evaluate the key of hash map dynamically (run time).

function isFunction(func){
    return Object.prototype.toString.call(func) === '[object Function]';
}

/**
 * This function converts an array to hash map
 * @param {String | function} key describes the key to be evaluated in each object to use as key for hasmap
 * @returns Object
 * @Example 
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap("id")
        Returns :- Object {123: Object, 345: Object}

        [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap(function(obj){return obj.id+1})
        Returns :- Object {124: Object, 346: Object}
 */
Array.prototype.toHashMap = function(key){
    var _hashMap = {}, getKey = isFunction(key)?key: function(_obj){return _obj[key];};
    this.forEach(function (obj){
        _hashMap[getKey(obj)] = obj;
    });
    return _hashMap;
};

You can find the gist here : https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa


Using simple Javascript

var createMapFromList = function(objectList, property) {
    var objMap = {};
    objectList.forEach(function(obj) {
      objMap[obj[property]] = obj;
    });
    return objMap;
  };
// objectList - the array  ;  property - property as the key

You can use Array.prototype.reduce() and actual JavaScript Map instead just a JavaScript Object.

let keyValueObjArray = [
  { key: 'key1', val: 'val1' },
  { key: 'key2', val: 'val2' },
  { key: 'key3', val: 'val3' }
];

let keyValueMap = keyValueObjArray.reduce((mapAccumulator, obj) => {
  // either one of the following syntax works
  // mapAccumulator[obj.key] = obj.val;
  mapAccumulator.set(obj.key, obj.val);

  return mapAccumulator;
}, new Map());

console.log(keyValueMap);
console.log(keyValueMap.size);

What is different between Map And Object?
Previously, before Map was implemented in JavaScript, Object has been used as a Map because of their similar structure.
Depending on your use case, if u need to need to have ordered keys, need to access the size of the map or have frequent addition and removal from the map, a Map is preferable.

Quote from MDN document:
Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:

  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.
  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.
  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.
  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion and iterating over them.
  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can be bypassed by using map = Object.create(null), but this is seldom done.
  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.

Using ES6 Map (pretty well supported), you can try this:

_x000D_
_x000D_
var arr = [_x000D_
    { key: 'foo', val: 'bar' },_x000D_
    { key: 'hello', val: 'world' }_x000D_
];_x000D_
_x000D_
var result = new Map(arr.map(i => [i.key, i.val]));_x000D_
_x000D_
// When using TypeScript, need to specify type:_x000D_
// var result = arr.map((i): [string, string] => [i.key, i.val])_x000D_
_x000D_
// Unfortunately maps don't stringify well.  This is the contents in array form._x000D_
console.log("Result is: " + JSON.stringify([...result])); _x000D_
// Map {"foo" => "bar", "hello" => "world"}
_x000D_
_x000D_
_x000D_


If you want to convert to the new ES6 Map do this:

var kvArray = [['key1', 'value1'], ['key2', 'value2']];
var myMap = new Map(kvArray);

Why should you use this type of Map? Well that is up to you. Take a look at this.


es2015 version:

const myMap = new Map(objArray.map(obj => [ obj.key, obj.val ]));

With lodash, this can be done using keyBy:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = _.keyBy(arr, o => o.key);

console.log(result);
// Object {foo: Object, hello: Object}

This is what I'm doing in TypeScript I have a little utils library where I put things like this

export const arrayToHash = (array: any[], id: string = 'id') => 
         array.reduce((obj, item) =>  (obj[item[id]] = item , obj), {})

usage:

const hash = arrayToHash([{id:1,data:'data'},{id:2,data:'data'}])

or if you have a identifier other than 'id'

const hash = arrayToHash([{key:1,data:'data'},{key:2,data:'data'}], 'key')

A small improvement on the reduce usage:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = arr.reduce((map, obj) => ({
    ...map,
    [obj.key] = obj.val
}), {});

console.log(result);
// { foo: 'bar', hello: 'world' }

Using ES6 spread + Object.assign:

array = [{key: 'a', value: 'b', redundant: 'aaa'}, {key: 'x', value: 'y', redundant: 'zzz'}]

const hash = Object.assign({}, ...array.map(s => ({[s.key]: s.value})));

console.log(hash) // {a: b, x: y}

Using the spread operator:

const result = arr.reduce(
    (accumulator, target) => ({ ...accumulator, [target.key]: target.val }),
    {});

Demonstration of the code snippet on jsFiddle.


This is fairly trivial to do with Array.prototype.reduce:

_x000D_
_x000D_
var arr = [_x000D_
    { key: 'foo', val: 'bar' },_x000D_
    { key: 'hello', val: 'world' }_x000D_
];_x000D_
_x000D_
var result = arr.reduce(function(map, obj) {_x000D_
    map[obj.key] = obj.val;_x000D_
    return map;_x000D_
}, {});_x000D_
_x000D_
console.log(result);_x000D_
// { foo:'bar', hello:'world' }
_x000D_
_x000D_
_x000D_

Note: Array.prototype.reduce() is IE9+, so if you need to support older browsers you will need to polyfill it.


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to hashmap

How to split a string in two and store it in a field Printing a java map Map<String, Object> - How? Hashmap with Streams in Java 8 Streams to collect value of Map How to convert String into Hashmap in java Convert object array to hash map, indexed by an attribute value of the Object HashMap - getting First Key value The type java.util.Map$Entry cannot be resolved. It is indirectly referenced from required .class files Sort Go map values by keys Print all key/value pairs in a Java ConcurrentHashMap creating Hashmap from a JSON String