Converti array di oggetti in hash map, indicizzato da un valore di attributo dell'Oggetto


305

Caso d'uso

Il caso d'uso è convertire una matrice di oggetti in una mappa hash basata su stringa o funzione fornita per valutare e utilizzare come chiave nella mappa hash e valore come oggetto stesso. Un caso comune di utilizzo di questo è la conversione di una matrice di oggetti in una mappa hash di oggetti.

Codice

Di seguito è riportato un piccolo frammento in JavaScript per convertire una matrice di oggetti in una mappa hash, indicizzata dal valore dell'attributo dell'oggetto. È possibile fornire una funzione per valutare dinamicamente la chiave della mappa hash (tempo di esecuzione). Spero che questo aiuti qualcuno in futuro.

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

Puoi trovare l'essenza qui: Converte Array of Objects in HashMap .


Puoi usare JavaScript Map invece di Object. Scopri stackoverflow.com/a/54246603/5042169
Jun711

Risposte:


471

Questo è abbastanza banale da fare con 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' }

Nota: Array.prototype.reduce() è IE9 +, quindi se è necessario supportare i browser più vecchi è necessario eseguirne il polyfill.


47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});Per i fan della prima fila ES6: D
Teodor Sandu

31
@Mtz Per ES6 gli appassionati di una sola riga, la risposta di mateuscb sotto è molto più piccolo e più pulito: result = new Map(arr.map(obj => [obj.key, obj.val]));. Ancora più importante, rende super chiaro che viene restituita una mappa.
Ryan Shillington,

2
@RyanShillington ci troviamo nel contesto di una risposta, Array.prototype.reducecome proposto da jmar777. Mapè davvero più corto ma è una cosa diversa. Mi mantenevo in linea con l'intento originale. Ricorda che questo non è un forum, potresti voler leggere di più sulla struttura SO Q / A.
Teodor Sandu,

2
@Mtz Abbastanza giusto.
Ryan Shillington,

1
Questo non è ciò che è stato chiesto, IMHO. Il risultato corretto per la matrice mostrata sarebbe: { "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }. Si noti che ogni elemento originale deve essere mantenuto nella sua interezza . O utilizzando i dati del Q: {"345": {id:345, name:"kumar"}, ...}. FIX: Modifica il codice inmap[obj.key] = obj;
ToolmakerSteve

302

Utilizzando ES6 Map ( abbastanza ben supportato ), puoi provare questo:

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
È anche importante notare che per ottenere qualcosa da un Mapdevi usare result.get(keyName)al contrario di giusto result[keyName]. Si noti inoltre che qualsiasi oggetto può essere utilizzato come chiave e non solo come una stringa.
Simon_Weaver,

5
Un'altra versione di TypeScript sarebbe simile a questa: var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));che alcuni potrebbero trovare più facile da capire. as [string, string]Cast di tipo nota aggiunto.
AlexV,

Quando eseguo questo codice in Chrome v71, ricevo ancora un array:Result is: [["foo","bar"],["hello","world"]]
Jean-François Beauchamp

PS resultnon è un hash come richiesto dal PO.
Jean-François Beauchamp,

1
Un'altra versione dattiloscritta:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed,

39

Con lodash , questo può essere fatto usando 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}

Questa non è una hashmap
Pomme De Terre,

37

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

2
Perfetto, proprio quello di cui avevo bisogno;)
Pierre,

1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));ho dovuto fare questa modifica per lavorare con dattiloscritto.
ruwan800,

24

Utilizzando l'operatore spread:

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

Dimostrazione dello snippet di codice su jsFiddle .


7
Sono esattamente qui per questo! in che modo l'operatore di spread esegue il vecchio modo normale di assegnare semplicemente la nuova chiave e restituire l'accumulatore? dal momento che sta creando una nuova copia ogni volta, quindi spread funzionerà male !
AMTourky,

1
ora ti diffondi in ogni iterazione. Dovrebbe essere sicuro mutare nel riduttore. `` `const result = arr.reduce ((accumulatore, target) => {accumulator [target.key]: target.val; return accumulator}, {}); ``
MTJ

17

È possibile utilizzare Array.prototype.reduce () e la mappa JavaScript effettiva invece solo un oggetto 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);

Qual è la differenza tra Mappa e oggetto?
In precedenza, prima dell'implementazione di Map in JavaScript, Object era stato utilizzato come Map a causa della sua struttura simile.
A seconda del caso d'uso, se è necessario disporre di chiavi ordinate, è necessario accedere alle dimensioni della mappa o avere frequenti aggiunte e rimozione dalla mappa, è preferibile una mappa.

Citazione dal documento MDN : gli
oggetti sono simili a Maps in quanto entrambi consentono di impostare chiavi su valori, recuperare tali valori, eliminare chiavi e rilevare se qualcosa è memorizzato in una chiave. Per questo motivo (e perché non c'erano alternative integrate), gli oggetti sono stati storicamente usati come mappe; tuttavia, ci sono importanti differenze che rendono preferibile l'utilizzo di una mappa in alcuni casi:

  • Le chiavi di un oggetto sono stringhe e simboli, mentre possono avere qualsiasi valore per una mappa, comprese funzioni, oggetti e qualsiasi primitiva.
  • Le chiavi in ​​Mappa sono ordinate mentre le chiavi aggiunte all'oggetto non lo sono. Pertanto, quando si scorre su di esso, un oggetto Mappa restituisce le chiavi in ​​ordine di inserimento.
  • Puoi ottenere facilmente le dimensioni di una mappa con la proprietà size, mentre il numero di proprietà in un oggetto deve essere determinato manualmente.
  • Una mappa è iterabile e può quindi essere iterata direttamente, mentre l'iterazione su un oggetto richiede l'ottenimento delle sue chiavi in ​​qualche modo e l'iterazione su di essi.
  • Un oggetto ha un prototipo, quindi ci sono chiavi predefinite nella mappa che potrebbero scontrarsi con le tue chiavi se non stai attento. A partire da ES5 questo può essere aggirato usando map = Object.create (null), ma raramente viene fatto.
  • Una mappa può funzionare meglio in scenari che prevedono l'aggiunta e la rimozione frequenti di coppie di chiavi.

1
Manca una freccia. Cambio (mapAccumulator, obj) {...}per(mapAccumulator, obj) => {...}
maggio

15

Puoi usare il nuovo Object.fromEntries()metodo.

Esempio:

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}


12

versione es2015:

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

4

Questo è quello che sto facendo in TypeScript Ho una piccola libreria utils in cui metto cose come questa

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

utilizzo:

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

o se hai un identificatore diverso da "id"

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

Se vuoi usare un oggetto come chiave, dovrai usare Map invece di Object perché typescript non ti permetterà di usare gli oggetti come chiavi
Dany Dhondt,

3

Ci sono modi migliori per farlo, come spiegato da altri poster. Ma se voglio attenermi al puro JS e alla vecchia maniera allora eccolo qui:

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

si consiglia di utilizzare il metodo di riduzione rispetto a questo metodo. Ho voglia di usare questo metodo. è semplice e facile vedere tutto.
Santhosh Yedidi,

Adoro questo approccio. Penso che a volte il codice più semplice sia il migliore. Al giorno d'oggi le persone sono disattivate dalla mutabilità, ma fintanto che è contenuta, la mutabilità è in realtà piuttosto impressionante e performante.
Luis Aceituno,

3

Se si desidera convertire nella nuova mappa ES6, procedere come segue:

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

Perché dovresti usare questo tipo di mappa? Bene, dipende da te. Dai un'occhiata a questo .


2

Utilizzando Javascript semplice

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
non sta usando .map (...) inutile in questo esempio perché non ci restituisci nulla? Suggerirei Ognuno in questo caso.
cuddlecheek,

2

Con 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

Un piccolo miglioramento reducesull'uso:

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

È più veloce dell'altra risposta?
orad,

Probabilmente @orad non diffonde l'accumulatore e crea un nuovo oggetto ogni iterazione.
Luckylooke,

1

provare

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


0

Di seguito è riportato un piccolo frammento che ho creato in JavaScript per convertire array di oggetti in hash map, indicizzati dal valore dell'attributo dell'oggetto. È possibile fornire una funzione per valutare dinamicamente la chiave della mappa hash (tempo di esecuzione).

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

Puoi trovare l'essenza qui: https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa


11
Per favore, per favore, per favore non raccomandare l'estensione Array.prototype!
jmar777,

Ah! Capisco. Inizialmente pensavo che questa fosse una risposta proposta :)
jmar777,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.