Konversi array objek ke peta hash, diindeks oleh nilai atribut Object

305

Gunakan Kasing

Use case adalah untuk mengubah array objek menjadi peta hash berdasarkan string atau fungsi yang disediakan untuk mengevaluasi dan menggunakan sebagai kunci dalam peta hash dan nilai sebagai objek itu sendiri. Kasus umum menggunakan ini adalah mengubah array objek menjadi peta objek hash.

Kode

Berikut ini adalah cuplikan kecil dalam JavaScript untuk mengonversi array objek ke peta hash, diindeks oleh nilai atribut objek. Anda dapat memberikan fungsi untuk mengevaluasi kunci dari peta hash secara dinamis (run time). Semoga ini bisa membantu seseorang di masa depan.

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

Anda dapat menemukan intinya di sini: Konversi Array Objek ke HashMap .

Naveen I
sumber
Anda dapat menggunakan Peta JavaScript alih-alih Obyek. Lihat stackoverflow.com/a/54246603/5042169
Jun711

Jawaban:

471

Ini cukup sepele untuk dilakukan dengan 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' }

Catatan: Array.prototype.reduce() adalah IE9 +, jadi jika Anda perlu mendukung peramban yang lebih lama, Anda perlu mengisinya.

jmar777
sumber
47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});Untuk penggemar one-liner ES6: D
Teodor Sandu
31
@Mtz Untuk ES6 penggemar satu baris, respon mateuscb di bawah ini jauh lebih kecil dan lebih bersih: result = new Map(arr.map(obj => [obj.key, obj.val]));. Yang paling penting, ini membuatnya sangat jelas bahwa peta sedang dikembalikan.
Ryan Shillington
2
@RyanShillington kita berada dalam konteks jawaban di sini, Array.prototype.reduceseperti yang diusulkan oleh jmar777. Mapmemang lebih pendek tapi itu hal yang berbeda. Saya tetap sejalan dengan maksud aslinya. Ingat ini bukan forum, Anda mungkin ingin membaca lebih lanjut tentang struktur T / J SO.
Teodor Sandu
2
@ Mtz Cukup adil.
Ryan Shillington
1
Ini bukan yang diminta, IMHO. Hasil yang benar untuk array ditampilkan akan menjadi: { "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }. Perhatikan bahwa setiap elemen asli harus disimpan secara keseluruhan . Atau menggunakan data Q: {"345": {id:345, name:"kumar"}, ...}. FIX: Ubah kode menjadimap[obj.key] = obj;
ToolmakerSteve
302

Menggunakan ES6 Map ( didukung dengan sangat baik ), Anda dapat mencoba ini:

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
sumber
4
Juga penting untuk dicatat bahwa untuk mendapatkan sesuatu dari MapAnda perlu menggunakan yang result.get(keyName)bertentangan dengan adil result[keyName]. Perhatikan juga bahwa objek apa pun dapat digunakan sebagai kunci dan bukan hanya string.
Simon_Weaver
5
Versi TypeScript lain akan terlihat seperti ini: var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));yang mungkin lebih mudah dimengerti oleh beberapa orang. Catatan as [string, string]jenis pemeran ditambahkan.
AlexV
Ketika saya menjalankan kode ini di Chrome v71, saya masih mendapatkan sebuah array:Result is: [["foo","bar"],["hello","world"]]
Jean-François Beauchamp
PS resultbukan hash seperti yang diminta oleh OP.
Jean-François Beauchamp
1
Versi naskah lain:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed
39

Dengan lodash , ini bisa dilakukan menggunakan 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}
splintor
sumber
Itu bukan hashmap
Pomme De Terre
37

Menggunakan 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
sumber
2
Sempurna, hanya apa yang saya butuhkan;)
Pierre
1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));harus melakukan perubahan ini untuk bekerja dengan naskah.
ruwan800
24

Menggunakan operator spread:

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

Demonstrasi potongan kode di jsFiddle .

Pedro Lopes
sumber
7
Saya tepat di sini karena ini! bagaimana operator spread melakukan terhadap cara lama yang biasa hanya menetapkan kunci baru dan mengembalikan akumulator? karena itu membuat salinan baru setiap kali, maka penyebaran akan berkinerja buruk !
AMTourky
1
sekarang Anda menyebar di setiap iterasi. Seharusnya aman untuk bermutasi dalam peredam. `` `const result = arr.reduce ((akumulator, target) => {akumulator [target.key]: target.val; return akumulator}, {}); `` `
MTJ
17

Anda dapat menggunakan Array.prototype.reduce () dan Peta JavaScript yang sebenarnya, bukan hanya Obyek 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);

Apa perbedaan antara Map dan Object?
Sebelumnya, sebelum Peta diimplementasikan dalam JavaScript, Objek telah digunakan sebagai Peta karena strukturnya yang serupa.
Tergantung pada kasus penggunaan Anda, jika Anda perlu memiliki kunci yang dipesan, perlu mengakses ukuran peta atau memiliki penambahan dan penghapusan yang sering dari peta, Peta lebih disukai.

Kutipan dari dokumen MDN :
Objek mirip dengan Maps yang memungkinkan Anda mengatur kunci ke nilai, mengambil nilai-nilai itu, menghapus kunci, dan mendeteksi apakah ada sesuatu yang disimpan pada kunci. Karena ini (dan karena tidak ada alternatif bawaan), Objek telah digunakan sebagai Maps secara historis; namun, ada perbedaan penting yang membuat penggunaan Peta lebih disukai dalam kasus-kasus tertentu:

  • Kunci dari suatu Objek adalah String dan Simbol, sedangkan mereka dapat berupa nilai apa pun untuk Peta, termasuk fungsi, objek, dan primitif apa pun.
  • Kunci dalam Peta diurutkan sedangkan kunci yang ditambahkan ke objek tidak. Jadi, ketika iterasi di atasnya, objek Peta mengembalikan kunci dalam urutan penyisipan.
  • Anda bisa mendapatkan ukuran Peta dengan mudah dengan properti ukuran, sedangkan jumlah properti di Objek harus ditentukan secara manual.
  • Peta adalah iterable dan dengan demikian dapat langsung iterated, sedangkan iterasi atas suatu Object memerlukan memperoleh kuncinya dalam beberapa cara dan iterasi atas mereka.
  • Objek memiliki prototipe, jadi ada kunci default di peta yang bisa bertabrakan dengan kunci Anda jika Anda tidak hati-hati. Pada ES5 ini dapat dilewati dengan menggunakan map = Object.create (null), tetapi ini jarang dilakukan.
  • Peta dapat tampil lebih baik dalam skenario yang melibatkan penambahan dan penghapusan pasangan kunci yang sering.
Jun711
sumber
1
Anda kehilangan panah. Ubah (mapAccumulator, obj) {...}untuk(mapAccumulator, obj) => {...}
mayid
15

Anda dapat menggunakan Object.fromEntries()metode baru .

Contoh:

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
sumber
12

versi es2015:

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

Ini yang saya lakukan di TypeScript. Saya punya perpustakaan kecil di mana saya meletakkan hal-hal seperti ini

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

pemakaian:

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

atau jika Anda memiliki pengidentifikasi selain 'id'

const hash = arrayToHash([{key:1,data:'data'},{key:2,data:'data'}], 'key')
Peter
sumber
Jika Anda ingin menggunakan objek sebagai kunci, Anda harus menggunakan Peta alih-alih Obyek karena naskah tidak akan membiarkan Anda menggunakan objek sebagai kunci
Dany Dhondt
3

Ada cara yang lebih baik untuk melakukan ini seperti yang dijelaskan oleh poster lain. Tetapi jika saya ingin tetap pada JS murni dan cara kuno maka di sini adalah:

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
sumber
disarankan untuk menggunakan metode pengurangan daripada metode ini. Saya merasa ingin menggunakan metode ini. itu sederhana dan mudah untuk melihat semuanya.
Santhosh Yedidi
Saya suka pendekatan ini. Saya pikir kadang-kadang kode paling sederhana adalah yang terbaik. Orang-orang dimatikan oleh sifat berubah-ubah saat ini, tetapi selama itu terkandung, sifat berubah-ubah sebenarnya cukup mengagumkan dan performan.
Luis Aceituno
3

Jika Anda ingin mengonversi ke Peta ES6 baru, lakukan ini:

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

Mengapa Anda harus menggunakan jenis Peta ini? Nah itu terserah kamu. Lihatlah ini .

Tiago Bértolo
sumber
2

Menggunakan Javascript sederhana

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
sumber
3
tidak menggunakan .map (...) tidak ada gunanya dalam contoh ini karena Anda tidak mengembalikan apa pun di dalamnya? Saya akan menyarankan forEach dalam kasus ini.
cuddlecheek
2

Dengan 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
sumber
1

Peningkatan kecil pada reducepenggunaan:

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
sumber
Apakah lebih cepat dari jawaban yang lain ?
orad
@orad mungkin tidak karena menyebarkan akumulator dan membuat objek baru setiap iterasi.
Luckylooke
1

mencoba

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

Kamil Kiełczewski
sumber
0

Berikut ini adalah potongan kecil yang saya buat dalam javascript untuk mengubah array objek ke peta hash, diindeks oleh nilai atribut objek. Anda dapat memberikan fungsi untuk mengevaluasi kunci dari peta hash secara dinamis (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;
};

Anda dapat menemukan intinya di sini: https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa

Naveen I
sumber
11
Tolong, tolong, tolong jangan merekomendasikan memperpanjang Array.prototype!
jmar777
Ahh, begitu. Awalnya saya pikir ini adalah jawaban yang diajukan :)
jmar777