オブジェクト配列を、オブジェクトの属性値によってインデックス付けされたハッシュマップに変換します


305

使用事例

ユースケースは、評価してハッシュマップのキーとして使用し、オブジェクト自体として値を提供するために提供される文字列または関数に基づいて、オブジェクトの配列をハッシュマップに変換することです。これを使用する一般的なケースは、オブジェクトの配列をオブジェクトのハッシュマップに変換することです。

コード

以下は、オブジェクトの属性値によってインデックスが付けられたオブジェクトの配列をハッシュマップに変換するJavaScriptの小さなスニペットです。ハッシュマップのキーを動的に(実行時に)評価する関数を提供できます。これが将来誰かを助けることを願っています。

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;
};

あなたはここで要点を見つけることができます:オブジェクトの配列をHashMapに変換します。


Objectの代わりにJavaScript Mapを使用できます。チェックアウトstackoverflow.com/a/54246603/5042169
Jun711

回答:


471

これは次のことを行うにはかなり簡単ですArray.prototype.reduce

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

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

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

注: Array.prototype.reduce()はIE9 +であるため、古いブラウザをサポートする必要がある場合は、それをポリフィルする必要があります。


47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});ES6ワンライナーファンの場合:D
Teodor Sandu

31
@Mtz ES6の1行のファンの場合、以下のmateuscbの応答ははるかに小さく、わかりやすくなっています result = new Map(arr.map(obj => [obj.key, obj.val]));。最も重要なのは、マップが返されていることが非常に明確になることです。
Ryan Shillington

2
@RyanShillingtonはArray.prototype.reduce、jmar777によって提案された回答のコンテキストにあります。Map確かに短いですが、それは別のものです。私は当初の意図に沿っていました。これはフォーラムではないことを覚えておいてください。SOQ / A構造の詳細を読むことをお勧めします。
テオドールサンドゥ

2
@Mtz十分だ。
ライアン

1
これは求められたものではありません、私見。表示される配列の正しい結果は次のようになります{ "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }。元の各要素は完全に保持する必要があることに注意してください。または、Qのデータを使用します{"345": {id:345, name:"kumar"}, ...}。FIX:なるように変更コードmap[obj.key] = obj;
ToolmakerSteve

302

ES6 Mapかなりよくサポートされています)を使用して、これを試すことができます:

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

var result = new Map(arr.map(i => [i.key, i.val]));

// When using TypeScript, need to specify type:
// var result = arr.map((i): [string, string] => [i.key, i.val])

// Unfortunately maps don't stringify well.  This is the contents in array form.
console.log("Result is: " + JSON.stringify([...result])); 
// Map {"foo" => "bar", "hello" => "world"}


4
から何かを取得するには、Mapとはresult.get(keyName)対照的に使用する必要があることに注意してくださいresult[keyName]。また、文字列だけでなく、任意のオブジェクトをキーとして使用できることにも注意してください。
Simon_Weaver

5
TypeScriptの別のバージョンは次のようにvar result = new Map(arr.map(i => [i.key, i.val] as [string, string]));なります。ノートas [string, string]型キャストが追加されました。
AlexV

Chrome v71でこのコードを実行しても、まだ配列が取得されています。Result is: [["foo","bar"],["hello","world"]]
Jean-FrançoisBeauchamp

PS resultは、OPによって要求されたハッシュではありません。
ジャン=フランソワ・ビーチャム

1
別のtypescriptバージョン:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed

39

ではlodash、これは使用して行うことができ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}

これはハッシュマップではありません
Pomme De Terre

37

ES6スプレッド+ 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}

2
完璧、まさに私が必要としたもの;)
ピエール

1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));typescriptを使用するには、この変更を行う必要がありました。
ruwan800

24

スプレッド演算子の使用:

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

jsFiddleのコードスニペットのデモ。


7
これが原因で私はここにいます!どのようにスプレッド演算子が新しいキーを割り当ててアキュムレータを返す通常の古い方法をもう一度実行しますか?毎回新しいコピーを作成するため、スプレッドのパフォーマンスは低下します。
AMTourky 2018

1
今度はすべての反復で広がります。レデューサーで変更しても安全です。`` `const result = arr.reduce((accumulator、target)=> {accumulator [target.key]:target.val; return accumulator}、{}); `` `
MTJ

17

JavaScript オブジェクトの代わりに、Array.prototype.reduce()と実際のJavaScript マップを使用できます。

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);

マップとオブジェクトの違いは何ですか?
以前は、JavaScriptでMapが実装される前は、Objectは構造が似ているためMapとして使用されていました。
ユースケースに応じて、キーを順序付けする必要がある場合、マップのサイズにアクセスする必要がある場合、またはマップに頻繁に追加および削除する必要がある場合は、マップが推奨されます。

MDNドキュメントからの引用:
オブジェクトはマップに似ていますが、どちらもキーを値に設定し、それらの値を取得し、キーを削除し、キーに何かが格納されているかどうかを検出できます。このため(そして組み込みの代替手段がなかったため)、オブジェクトは歴史的にマップとして使用されてきました。ただし、特定の場合にマップの使用を推奨する重要な違いがあります。

  • オブジェクトのキーは文字列とシンボルですが、関数、オブジェクト、プリミティブなど、マップの任意の値を使用できます。
  • Mapのキーは順序付けされていますが、オブジェクトに追加されたキーは順序付けされていません。したがって、それを反復すると、Mapオブジェクトは挿入順にキーを返します。
  • オブジェクトのプロパティの数は手動で決定する必要がありますが、サイズプロパティを使用するとマップのサイズを簡単に取得できます。
  • Mapは反復可能であるため、直接反復できますが、オブジェクトを反復するには、何らかの方法でキーを取得し、それらを反復する必要があります。
  • オブジェクトにはプロトタイプがあるため、注意しないとキーと衝突する可能性のあるデフォルトのキーがマップ内にあります。ES5以降、これはmap = Object.create(null)を使用してバイパスできますが、ほとんど行われません。
  • キーペアを頻繁に追加および削除するシナリオでは、マップのパフォーマンスが向上する場合があります。

1
矢印がありません。変更(mapAccumulator, obj) {...}のために(mapAccumulator, obj) => {...}
mayid


12

es2015バージョン:

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

4

これは私がTypeScriptでやっていることです私はこのようなものを置く小さなutilsライブラリを持っています

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

使用法:

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

または「id」以外の識別子がある場合

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

キーとしてオブジェクトを使用する場合は、typescriptではオブジェクトをキーとして使用できないため、ObjectではなくMapを使用する必要があります
Dany Dhondt

3

他のポスターで説明されているように、これを行うにはより良い方法があります。しかし、私が純粋なJSと昔ながらの方法に固執したい場合は、ここにあります:

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);

この方法よりも削減方法を使用することをお勧めしますか?私はこの方法を使用したい気がします。シンプルで見やすい。
Santhosh Yedidi

私はこのアプローチが大好きです。私は時々最も単純なコードが最良だと思います。人々は今日、可変性によって気を失っていますが、それが含まれている限り、可変性は実際にはかなり素晴らしく、高性能です。
Luis Aceituno

3

新しいES6 マップに変換する場合は、次のようにします。

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

なぜこのタイプのマップを使用する必要があるのですか?まあそれはあなた次第です。これを見てください。


2

単純な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

3
この例では何も返さないので、.map(...)を使用しても意味がありませんか?この場合はforEachをお勧めします。
cuddlecheek 2017

2

lodash

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

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

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

1

reduce使用法の小さな改善:

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' }

他の答えよりも速いですか?
orad

@oradはアキュムレータを拡散し、反復ごとに新しいオブジェクトを作成するため、おそらくそうではありません。
Luckylooke

1

試す

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


0

以下は、オブジェクトの配列をハッシュマップに変換するためにJavaScriptで作成した小さなスニペットで、オブジェクトの属性値によってインデックスが付けられています。ハッシュマップのキーを動的に(実行時に)評価する関数を提供できます。

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;
};

あなたはここで要旨を見つけることができます:https//gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa


11
どうぞ、延長してはいけませんArray.prototype
jmar777 2014年

ええ、わかりました。私は当初これが提案された回答だと思っていました:)
jmar777 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.