Konvertieren Sie das Objektarray in eine Hash-Map, die durch einen Attributwert des Objekts indiziert ist

305

Anwendungsfall

Der Anwendungsfall besteht darin, ein Array von Objekten in eine Hash-Map zu konvertieren, die auf einer Zeichenfolge oder Funktion basiert, die zur Auswertung und Verwendung als Schlüssel in der Hash-Map und zum Wert als Objekt selbst bereitgestellt wird. Ein häufiger Fall bei dieser Verwendung ist das Konvertieren eines Arrays von Objekten in eine Hash-Map von Objekten.

Code

Das Folgende ist ein kleines Snippet in JavaScript, um ein Array von Objekten in eine Hash-Map zu konvertieren, die durch den Attributwert des Objekts indiziert ist. Sie können eine Funktion bereitstellen, um den Schlüssel der Hash-Map dynamisch auszuwerten (Laufzeit). Hoffe das hilft jemandem in Zukunft.

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

Das Wesentliche finden Sie hier: Konvertiert ein Array von Objekten in HashMap .

Naveen ich
quelle
Sie können JavaScript Map anstelle von Object verwenden. Check out stackoverflow.com/a/54246603/5042169
Jun711

Antworten:

471

Dies ist ziemlich trivial zu tun mit 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' }

Hinweis: Array.prototype.reduce() ist IE9 +. Wenn Sie also ältere Browser unterstützen müssen, müssen Sie diese mehrfach ausfüllen.

jmar777
quelle
47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});Für ES6 Einzeiler-Fans: D
Teodor Sandu
31
@Mtz Für einzeilige ES6-Fans ist die Antwort von mateuscb unten viel kleiner und sauberer : result = new Map(arr.map(obj => [obj.key, obj.val]));. Vor allem macht es sehr deutlich, dass eine Karte zurückgegeben wird.
Ryan Shillington
2
@ RyanShillington Wir befinden uns hier im Kontext einer Antwort, Array.prototype.reducewie von jmar777 vorgeschlagen. Mapist zwar kürzer, aber es ist eine andere Sache. Ich hielt mich an die ursprüngliche Absicht. Denken Sie daran, dass dies kein Forum ist. Vielleicht möchten Sie mehr über die SO Q / A-Struktur lesen.
Teodor Sandu
2
@ Mtz Fair genug.
Ryan Shillington
1
Dies ist nicht das, wonach gefragt wurde, IMHO. Das korrekte Ergebnis für das angezeigte Array wäre : { "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }. Beachten Sie, dass jedes Originalelement in seiner Gesamtheit beibehalten werden sollte . Oder mit den Daten des Q : {"345": {id:345, name:"kumar"}, ...}. UPDATE: Ändern Sie den Code inmap[obj.key] = obj;
ToolmakerSteve
302

Mit ES6 Map ( ziemlich gut unterstützt ) können Sie Folgendes versuchen:

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

mateuscb
quelle
4
Auch wichtig zu beachten , dass etwas , um aus ein MapSie verwenden müssen, result.get(keyName)als nur gegenüber result[keyName]. Beachten Sie auch, dass jedes Objekt als Schlüssel und nicht nur als Zeichenfolge verwendet werden kann.
Simon_Weaver
5
Eine andere TypeScript- Version würde folgendermaßen aussehen: var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));Einige finden sie möglicherweise leichter zu verstehen. Notentyp as [string, string]Besetzung hinzugefügt.
AlexV
Wenn ich diesen Code in Chrome v71 ausführe, erhalte ich immer noch ein Array:Result is: [["foo","bar"],["hello","world"]]
Jean-François Beauchamp
PS resultist kein vom OP angeforderter Hash.
Jean-François Beauchamp
1
Eine andere Typoskript-Version:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed
39

Mit lodash kann dies mit keyBy erfolgen :

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}
Schiene
quelle
Das ist keine Hashmap
Pomme De Terre
37

Verwenden von 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}
shuk
quelle
2
Perfekt, genau das, was ich brauchte;)
Pierre
1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));musste diese Änderung vornehmen, um mit Typoskript zu arbeiten.
Ruwan800
24

Verwenden des Spread-Operators:

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

Demonstration des Code-Snippets auf jsFiddle .

Pedro Lopes
quelle
7
Ich bin genau deshalb hier! Wie verhält sich der Spread-Operator gegenüber der normalen alten Methode, einfach den neuen Schlüssel zuzuweisen und den Akku zurückzugeben? Da jedes Mal eine neue Kopie erstellt wird, ist die Verbreitung schlecht !
AMTourky
1
Jetzt verbreiten Sie sich in jeder Iteration. Es sollte sicher sein, im Reduktionsmittel zu mutieren. `` `const result = arr.reduce ((Akkumulator, Ziel) => {Akkumulator [Zielschlüssel]: target.val; Akkumulator zurückgeben}, {}); `` `
MTJ
17

Sie können Array.prototype.reduce () und die tatsächliche JavaScript- Map anstelle eines JavaScript- Objekts verwenden .

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

Was ist anders zwischen Karte und Objekt?
Zuvor, bevor Map in JavaScript implementiert wurde, wurde Object aufgrund seiner ähnlichen Struktur als Map verwendet.
Abhängig von Ihrem Anwendungsfall ist eine Karte vorzuziehen, wenn Sie Schlüssel bestellt haben müssen, auf die Größe der Karte zugreifen müssen oder häufig Karten hinzufügen und daraus entfernen müssen.

Zitat aus MDN-Dokument :
Objekte ähneln Maps dahingehend, dass Sie beide Schlüssel auf Werte setzen, diese Werte abrufen, Schlüssel löschen und erkennen können, ob etwas an einem Schlüssel gespeichert ist. Aus diesem Grund (und weil es keine integrierten Alternativen gab) wurden Objekte in der Vergangenheit als Karten verwendet. Es gibt jedoch wichtige Unterschiede, die die Verwendung einer Karte in bestimmten Fällen vorzuziehen machen:

  • Die Schlüssel eines Objekts sind Zeichenfolgen und Symbole, während sie ein beliebiger Wert für eine Karte sein können, einschließlich Funktionen, Objekte und beliebiger Grundelemente.
  • Die Schlüssel in Map sind geordnet, Schlüssel, die dem Objekt hinzugefügt wurden, nicht. Wenn ein Map-Objekt darüber iteriert, gibt es die Schlüssel in der Reihenfolge des Einfügens zurück.
  • Sie können die Größe einer Karte einfach mit der size-Eigenschaft ermitteln, während die Anzahl der Eigenschaften in einem Objekt manuell bestimmt werden muss.
  • Eine Karte ist iterierbar und kann daher direkt iteriert werden, während das Iterieren über ein Objekt das Erhalten seiner Schlüssel auf irgendeine Weise und das Iterieren über sie erfordert.
  • Ein Objekt hat einen Prototyp, daher gibt es Standardschlüssel in der Karte, die mit Ihren Schlüsseln kollidieren können, wenn Sie nicht vorsichtig sind. Ab ES5 kann dies mit map = Object.create (null) umgangen werden, dies wird jedoch selten durchgeführt.
  • Eine Karte kann in Szenarien mit häufigem Hinzufügen und Entfernen von Schlüsselpaaren eine bessere Leistung erzielen.
Jun711
quelle
1
Ihnen fehlt ein Pfeil. Ändern Sie (mapAccumulator, obj) {...}für(mapAccumulator, obj) => {...}
mayid
15

Sie können die neue Object.fromEntries()Methode verwenden.

Beispiel:

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

const hash = Object.fromEntries(
   array.map(e => [e.key, e.value])
)

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

Fabiano Taioli
quelle
12

es2015 version:

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

Dies ist, was ich in TypeScript mache. Ich habe eine kleine Utils-Bibliothek, in der ich solche Dinge platziere

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

Verwendung:

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

oder wenn Sie eine andere Kennung als 'id' haben

const hash = arrayToHash([{key:1,data:'data'},{key:2,data:'data'}], 'key')
Peter
quelle
Wenn Sie ein Objekt als Schlüssel verwenden möchten, müssen Sie Map anstelle von Object verwenden, da Sie mit Typoskript keine Objekte als Schlüssel verwenden können
Dany Dhondt,
3

Es gibt bessere Möglichkeiten, dies zu tun, wie auf anderen Postern erläutert. Aber wenn ich mich an reine JS und altmodische Art halten will, dann ist es hier:

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);
rhel.user
quelle
wird empfohlen, die Reduktionsmethode als diese Methode zu verwenden. Ich möchte diese Methode anwenden. Es ist einfach und leicht, alles zu sehen.
Santhosh Yedidi
Ich liebe diesen Ansatz. Ich denke manchmal ist der einfachste Code der beste. Menschen werden heutzutage durch Veränderlichkeit abgeschaltet, aber solange sie enthalten sind, ist Veränderlichkeit tatsächlich ziemlich großartig und performant.
Luis Aceituno
3

Wenn Sie auf die neue ES6- Karte konvertieren möchten, gehen Sie folgendermaßen vor:

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

Warum sollten Sie diese Art von Karte verwenden? Nun, das liegt an dir. Schauen Sie sich auf diese .

Tiago Bértolo
quelle
2

Mit einfachem 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
Partha Roy
quelle
3
Ist die Verwendung von .map (...) in diesem Beispiel nicht sinnlos, da Sie nichts zurückgeben? Ich würde für jeden in diesem Fall vorschlagen.
Cuddlecheek
2

Mit 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' }
Tho
quelle
1

Eine kleine Verbesserung der reduceNutzung:

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' }
Mor Shemesh
quelle
Ist es schneller als die andere Antwort?
Orad
@orad wahrscheinlich nicht, da es Akkumulator verbreitet und bei jeder Iteration ein neues Objekt erstellt.
Luckylooke
1

Versuchen

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

Kamil Kiełczewski
quelle
0

Das Folgende ist ein kleines Snippet, das ich in Javascript erstellt habe, um ein Array von Objekten in eine Hash-Map zu konvertieren, indiziert nach dem Attributwert des Objekts. Sie können eine Funktion bereitstellen, um den Schlüssel der Hash-Map dynamisch auszuwerten (Laufzeit).

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

Das Wesentliche finden Sie hier: https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa

Naveen ich
quelle
11
Bitte, bitte, bitte nicht verlängern Array.prototype!
jmar777
Ahh, ich verstehe. Ich dachte zunächst, dies sei eine vorgeschlagene Antwort :)
jmar777