Chuyển đổi mảng đối tượng thành bản đồ băm, được lập chỉ mục bởi một giá trị thuộc tính của Đối tượng


305

Ca sử dụng

Trường hợp sử dụng là để chuyển đổi một mảng các đối tượng thành bản đồ băm dựa trên chuỗi hoặc hàm được cung cấp để đánh giá và sử dụng làm khóa trong bản đồ băm và giá trị như chính đối tượng. Một trường hợp phổ biến của việc sử dụng này là chuyển đổi một mảng các đối tượng thành một bản đồ băm của các đối tượng.

Sau đây là một đoạn nhỏ trong JavaScript để chuyển đổi một mảng các đối tượng thành bản đồ băm, được lập chỉ mục bởi giá trị thuộc tính của đối tượng. Bạn có thể cung cấp một hàm để đánh giá khóa của bản đồ băm một cách linh hoạt (thời gian chạy). Hy vọng điều này sẽ giúp ai đó trong tương lai.

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

Bạn có thể tìm thấy ý chính ở đây: Chuyển đổi mảng đối tượng thành HashMap .


Bạn có thể sử dụng Bản đồ JavaScript thay vì Object. Kiểm tra stackoverflow.com/a/54246603/5042169
Jun711

Câu trả lời:


471

Điều này khá tầm thường để làm với 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' }

Lưu ý: Array.prototype.reduce() là IE9 +, vì vậy nếu bạn cần hỗ trợ các trình duyệt cũ hơn, bạn sẽ cần phải hoàn thành nó.


47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});Dành cho người hâm mộ một lớp ES6: D
Teodor Sandu

31
@Mtz Đối với người hâm mộ một dòng ES6, phản hồi của mateuscb bên dưới nhỏ hơn và gọn gàng hơn : result = new Map(arr.map(obj => [obj.key, obj.val]));. Quan trọng nhất, nó làm cho siêu rõ ràng rằng một bản đồ đang được trả lại.
Ryan Shillington

2
@RyanShillington chúng tôi đang ở trong một câu trả lời ở đây, Array.prototype.reducenhư được đề xuất bởi jmar777. Mapthực sự ngắn hơn nhưng đó là một điều khác biệt. Tôi đã giữ đúng với mục đích ban đầu. Hãy nhớ rằng đây không phải là một diễn đàn, bạn có thể muốn đọc thêm về cấu trúc SO Q / A.
Teodor Sandu

2
@Mtz đủ công bằng.
Ryan Shillington

1
Đây không phải là những gì được yêu cầu, IMHO. Kết quả chính xác cho mảng hiển thị sẽ là : { "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }. Lưu ý rằng mỗi yếu tố ban đầu nên được giữ nguyên . Hoặc sử dụng dữ liệu của Q : {"345": {id:345, name:"kumar"}, ...}. CỐ ĐỊNH: Thay đổi mã thànhmap[obj.key] = obj;
ToolmakerSteve

302

Sử dụng ES6 Map ( được hỗ trợ khá tốt ), bạn có thể thử điều này:

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
Cũng cần lưu ý rằng để có được một cái gì đó từ một Mapbạn cần phải sử dụng result.get(keyName)trái ngược với chỉ result[keyName]. Cũng lưu ý rằng bất kỳ đối tượng nào cũng có thể được sử dụng làm khóa chứ không chỉ là một chuỗi.
Simon_Weaver

5
Một phiên bản TypeScript khác sẽ giống như thế này: var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));mà một số người có thể thấy dễ hiểu hơn. Ghi chú as [string, string]loại thêm.
AlexV

Khi tôi chạy mã này trong Chrome v71, tôi vẫn nhận được một mảng:Result is: [["foo","bar"],["hello","world"]]
Jean-François Beauchamp

PS resultkhông phải là hàm băm theo yêu cầu của OP.
Jean-François Beauchamp

1
Một phiên bản bản thảo khác:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed

39

Với lodash , điều này có thể được thực hiện bằng 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}

Đó không phải là một hashmap
Pomme De Terre

37

Sử dụng ES6 lây lan + 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
Hoàn hảo, đúng như những gì tôi cần;)
Pierre

1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));đã phải thực hiện thay đổi này để làm việc với bản thảo.
ruwan800

24

Sử dụng toán tử trải rộng:

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

Trình diễn đoạn mã trên jsFiddle .


7
Tôi chính xác ở đây vì điều này! Làm thế nào toán tử trải rộng thực hiện lại cách cũ thông thường chỉ cần gán khóa mới và trả lại bộ tích lũy? vì nó tạo ra một bản sao mới mỗi lần, nên việc lan truyền sẽ hoạt động kém !
AMTourky

1
bây giờ bạn trải đều trong mỗi lần lặp. Nó sẽ được an toàn để đột biến trong giảm. `` `const result = Array.reduce ((tích lũy, đích) => {ắc quy [target.key]: target.val; return accumoder}, {}); `` `
MTJ

17

Bạn có thể sử dụng Array.prototype.reduce ()Bản đồ JavaScript thực tế thay vì chỉ là Đối tượng 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);

Có gì khác nhau giữa Bản đồ và Đối tượng?
Trước đây, trước khi Map được triển khai trong JavaScript, Object đã được sử dụng làm Map vì cấu trúc tương tự của chúng.
Tùy thuộc vào trường hợp sử dụng của bạn, nếu bạn cần phải có các phím được đặt hàng, cần truy cập vào kích thước của bản đồ hoặc thường xuyên thêm và xóa khỏi bản đồ, nên sử dụng Bản đồ.

Trích dẫn từ tài liệu MDN :
Các đối tượng tương tự như Bản đồ ở chỗ cả hai đều cho phép bạn đặt khóa thành giá trị, truy xuất các giá trị đó, xóa khóa và phát hiện xem có thứ gì được lưu trữ tại khóa hay không. Do điều này (và vì không có giải pháp thay thế tích hợp), các Đối tượng đã được sử dụng làm Bản đồ trong lịch sử; tuy nhiên, có những khác biệt quan trọng giúp sử dụng Bản đồ thích hợp hơn trong một số trường hợp nhất định:

  • Các khóa của Đối tượng là Chuỗi và Biểu tượng, trong khi chúng có thể là bất kỳ giá trị nào cho Bản đồ, bao gồm các chức năng, đối tượng và bất kỳ nguyên thủy nào.
  • Các khóa trong Bản đồ được sắp xếp trong khi các khóa được thêm vào đối tượng thì không. Do đó, khi lặp qua nó, một đối tượng Map sẽ trả về các khóa theo thứ tự chèn.
  • Bạn có thể dễ dàng lấy kích thước của Bản đồ với thuộc tính kích thước, trong khi số lượng thuộc tính trong Đối tượng phải được xác định thủ công.
  • Bản đồ là một lần lặp và do đó có thể được lặp lại trực tiếp, trong khi lặp lại trên một Đối tượng đòi hỏi phải có được các khóa của nó theo một cách thức nào đó và lặp đi lặp lại trên chúng.
  • Một đối tượng có một nguyên mẫu, do đó, có các khóa mặc định trong bản đồ có thể va chạm với các khóa của bạn nếu bạn không cẩn thận. Kể từ ES5, điều này có thể được bỏ qua bằng cách sử dụng map = Object.create (null), nhưng điều này hiếm khi được thực hiện.
  • Bản đồ có thể hoạt động tốt hơn trong các tình huống liên quan đến việc bổ sung và loại bỏ thường xuyên các cặp khóa.

1
Bạn đang thiếu một mũi tên. Thay đổi (mapAccumulator, obj) {...}cho(mapAccumulator, obj) => {...}
mayid

15

Bạn có thể sử dụng Object.fromEntries()phương pháp mới .

Thí dụ:

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

phiên bản es2015:

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

4

Đây là những gì tôi đang làm trong TypeScript Tôi có một thư viện utils nhỏ nơi tôi đặt những thứ như thế này

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

sử dụng:

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

hoặc nếu bạn có số nhận dạng khác với 'id'

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

Nếu bạn muốn sử dụng một đối tượng làm khóa, bạn sẽ phải sử dụng Bản đồ thay vì Đối tượng vì bản thảo sẽ không cho phép bạn sử dụng các đối tượng làm khóa
Dany Dhondt

3

Có nhiều cách tốt hơn để làm điều này như được giải thích bởi các áp phích khác. Nhưng nếu tôi muốn theo cách thuần túy của JS và ol 'thì đây là:

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

Có nên sử dụng phương pháp giảm hơn phương pháp này. Tôi cảm thấy như sử dụng phương pháp này. nó đơn giản và dễ dàng để nhìn thấy mọi thứ.
Santhosh Yedidi

Tôi thích cách tiếp cận này. Tôi nghĩ đôi khi mã đơn giản nhất là tốt nhất. Mọi người ngày nay đã bị tắt bởi tính đột biến, nhưng miễn là nó có, tính đột biến thực sự khá tuyệt vời và hiệu quả.
Luis Aceituno

3

Nếu bạn muốn chuyển đổi sang Bản đồ ES6 mới, hãy làm điều này:

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

Tại sao bạn nên sử dụng loại Bản đồ này? Vâng, đó là tùy thuộc vào bạn. Hãy nhìn vào điều này .


2

Sử dụng Javascript đơn giản

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
không sử dụng .map (...) trong ví dụ này vì bạn không trả lại bất cứ thứ gì trong đó? Tôi sẽ đề nghị forEach trong trường hợp này.
cuddlecheek

2

Với 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

Một cải tiến nhỏ về reducecách sử dụng:

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

Có nhanh hơn câu trả lời khác không?
orad

@orad có lẽ không phải vì nó lây lan bộ tích lũy và tạo đối tượng mới mỗi lần lặp.
Luckylooke

1

thử

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


0

Sau đây là đoạn mã nhỏ tôi đã tạo trong javascript để chuyển đổi mảng đối tượng thành bản đồ băm, được lập chỉ mục theo giá trị thuộc tính của đối tượng. Bạn có thể cung cấp một hàm để đánh giá khóa của bản đồ băm một cách linh hoạt (thời gian chạy).

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

Bạn có thể tìm thấy ý chính ở đây: https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa


11
Xin vui lòng, xin vui lòng, không khuyên bạn nên mở rộng Array.prototype!
jmar777

À, tôi hiểu rồi. Ban đầu tôi nghĩ đây là một câu trả lời được đề xuất :)
jmar777
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.