Làm thế nào để chuyển đổi một đối tượng thuần túy thành Bản đồ ES6?


128

Vì một số lý do tôi không thể tìm thấy điều đơn giản này trong tài liệu MDN (có thể tôi chỉ thiếu nó).

Tôi mong đợi điều này sẽ hoạt động:

const map = new Map({foo: 'bar'});

map.get('foo'); // 'bar'

... nhưng dòng đầu tiên ném TypeError: (var)[Symbol.iterator] is not a function

Làm cách nào để tạo Bản đồ từ một đối tượng thuần túy? Trước tiên tôi có thực sự phải chuyển đổi nó thành một mảng các cặp khóa-giá trị không?


2
FWIW, nó có thể đáng để chuyển câu trả lời được chấp nhận của bạn từ của tôi sang nils ' hoặc bergi's . Object.entriesthực sự là cách tiếp cận tốt hơn Object.keysvà cách tiếp cận hàm trình tạo của bergi trực tiếp hơn một chút so với Object.keyshoặc Object.entries.
TJ Crowder

Câu trả lời:


194

Có, hàm Maptạo nhận một mảng các cặp khóa-giá trị.

Object.entrieslà một phương thức tĩnh đối tượng mới có sẵn trong ES2017 (19.1.2.5) .

const map = new Map(Object.entries({foo: 'bar'}));

map.get('foo'); // 'bar'

Nó hiện được triển khai trên Firefox 46+ và Edge 14+ và các phiên bản Chrome mới hơn

Nếu bạn cần hỗ trợ các môi trường cũ hơn và chuyển đổi không phải là một lựa chọn cho bạn, hãy sử dụng polyfill, chẳng hạn như polyfill được georg đề xuất:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);

3
Một "polyfill" sẽ khá tầm thường:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg

4
Object.entriesđã hạ cánh xuống Node 7.x đúng cách (không có cờ) btw
Lee Benson

2
Không chỉ là Object.entries trong Node7, nó còn là một phần của đặc điểm kỹ thuật cuối cùng của ES2017. Do đó, điều này sẽ là câu trả lời được chấp nhận, IMO
AnilRedshift

1
@AnilRedshift - Đây hoặc câu trả lời của hàm máy phát , vì OP đã yêu cầu một giải pháp tránh trung gian.
TJ Crowder

2
Thật không may, nó sẽ không.
Nemanja Milosavljevic

78

Vui lòng xem câu trả lời của nils bằng cách sử dụngObject.entries và / hoặc câu trả lời của bergi bằng cách sử dụng hàm máy phát điện . Mặc dù Object.entrieschưa có trong thông số kỹ thuật khi câu hỏi được đặt ra, nhưng nó đã ở Giai đoạn 4 , rất an toàn để polyfill và sử dụng ngay cả vào tháng 4 năm 2016 (chỉ). (Xem thêm về các giai đoạn tại đây .) Và các chức năng của máy phát điện có trong ES2015. OP đã yêu cầu đặc biệt tránh những người trung gian, và mặc dù máy phát điện không hoàn toàn tránh được điều đó, nhưng nó hoạt động tốt hơn những điều dưới đây hoặc (một chút) Object.enties.

FWIW, sử dụng Object.entries:

  • Tạo một mảng các [name, value]mảng để chuyển đếnnew Map
  • Các Map tạo gọi một hàm trên mảng để lấy một trình lặp; mảng tạo và trả về một đối tượng số nguyên mảng.
  • Hàm Maptạo sử dụng đối tượng trình vòng lặp đó để lấy các mục nhập ( [name, value]mảng) và xây dựng bản đồ

Sử dụng máy phát điện:

  • Tạo đối tượng trình tạo do gọi hàm trình tạo
  • Hàm Maptạo gọi một hàm trên đối tượng trình tạo đó để lấy một trình lặp từ nó; đối tượng trình tạo trả về chính nó
  • Hàm Maptạo sử dụng đối tượng trình tạo (như một trình lặp) để lấy các mục nhập ( [name, value]mảng) và xây dựng bản đồ

Vì vậy: Ít hơn một trung gian (mảng từ Object.entries).

Tuy nhiên, việc sử dụng Object.entriesđơn giản hơn và việc tạo mảng đó không phải là vấn đề 99,999% thời gian. Vì vậy, thực sự, một trong hai. Nhưng cả hai đều tốt hơn những thứ bên dưới. :-)


Câu trả lời ban đầu:

Để khởi tạo a Map, bạn có thể sử dụng bất kỳ trình lặp nào trả về các cặp khóa / giá trị dưới dạng mảng, chẳng hạn như một mảng của mảng:

const map = new Map([
    ['foo', 'bar']
]);

Không có chuyển đổi tích hợp từ đối tượng sang bản đồ, nhưng nó dễ dàng được thực hiện với Object.keys:

const map = new Map();
let obj = {foo: 'bar'};
Object.keys(obj).forEach(key => {
    map.set(key, obj[key]);
});

Tất nhiên, bạn có thể tạo cho mình một hàm worker để xử lý điều đó:

function buildMap(obj) {
    let map = new Map();
    Object.keys(obj).forEach(key => {
        map.set(key, obj[key]);
    });
    return map;
}

Sau đó

const map = buildMap({foo: 'bar'});

Hoặc đây là một phiên bản trông giống l33t hơn (đó vẫn là một thứ?):

function buildMap(obj) {
    return Object.keys(obj).reduce((map, key) => map.set(key, obj[key]), new Map());
}

(Có, Map#settrả về tham chiếu bản đồ. Một số cho rằng đây là sự lạm dụng reduce.)

Hoặc chúng ta thực sự có thể vượt lên trên về sự mù mờ:

const buildMap = o => Object.keys(o).reduce((m, k) => m.set(k, o[k]), new Map());

Không, tôi sẽ không bao giờ làm điều đó thật. :-)


1
Fusion của @ Ohar và @ giải pháp TJCrowder của: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy Ngày

17
Xem new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier

câu trả lời Object.entries nên được ưa thích
Kir

@Kir - Đó hoặc máy phát điện , vâng. Xem bản chỉnh sửa của tôi.
TJ Crowder,

31

Trước tiên tôi có thực sự phải chuyển đổi nó thành một mảng các cặp khóa-giá trị không?

Không, một trình lặp của các mảng cặp khóa-giá trị là đủ. Bạn có thể sử dụng những điều sau để tránh tạo mảng trung gian:

function* entries(obj) {
    for (let key in obj)
        yield [key, obj[key]];
}

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'

Ví dụ tuyệt vời - chỉ là một lưu ý cho những người khác, bạn có thể muốn thực hiện if(!obj.hasOwnProperties(key)) continue;ngay sau điều kiện vòng lặp for để đảm bảo rằng bạn không mang lại các thuộc tính được kế thừa từ nguyên mẫu đối tượng (trừ khi bạn tin tưởng đối tượng, nhưng vẫn nên làm điều này khi lặp lại các đối tượng bằng cách sử dụng innhư một thói quen tốt).
puiu

2
@Puiu Không, bạn không nên, và đó là một thói quen xấu. Nếu bạn không tin tưởng vào đối tượng, bạn không phải tin tưởng nó .hasOwnPropertysở hữu là tốt, và bạn sẽ phải sử dụngObject.prototype.hasOwnProperty.call(obj, key)
Bergi

2
Vâng, tôi nghĩ không làm phiền là cách tốt nhất. Bạn nên tin tưởng tất cả các đối tượng có giá trị liệt kê (tức là khóa-giá trị-bản đồ) không có bất kỳ thuộc tính kế thừa có thể liệt kê nào. (Và có tất nhiên là tránh liệt kê các mảng ). Nếu bạn gặp một trường hợp hiếm hoi về việc liệt kê một cái gì đó khác và bạn thấy phiền, thì ít nhất bạn cũng nên thực hiện nó đúng cách call. Tất cả các quy ước ngoài đó được khuyến nghị obj.hasOwnProperties(key)dường như không có ý tưởng về những gì họ đang làm.
Bergi

3
Chuyển đổi một Objectthành một Maphoạt động tốn kém và OP đã yêu cầu một giải pháp đặc biệt mà không có chất trung gian. Vậy tại sao đây không phải là câu trả lời ngoại lệ? Chà, hỏi điều này có lẽ là vô ích, nó chỉ làm phiền tôi.

1
@tmvnty Bạn có thể cần phải viết chính tả loại function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Cũng yield [key, obj[key]] as const;sẽ giúp TypeScript nhận ra rằng nó mang lại các bộ giá trị, không phải mảng.
Bergi

11

Câu trả lời của Nils mô tả cách chuyển đổi đối tượng thành bản đồ, mà tôi thấy rất hữu ích. Tuy nhiên, OP cũng băn khoăn không biết thông tin này nằm ở đâu trong tài liệu MDN. Mặc dù nó có thể không có ở đó khi câu hỏi ban đầu được hỏi, nhưng bây giờ nó đã có trên trang MDN cho Object.entries () dưới tiêu đề Chuyển đổi một đối tượng thành một bản đồ , trong đó nêu rõ:

Chuyển đổi một đối tượng thành một bản đồ

Hàm new Map()tạo chấp nhận một có thể lặp lại của entries. Với Object.entries, bạn có thể dễ dàng chuyển đổi từ Objectsang Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)

1
Fusion của @ Ohar và @ giải pháp TJCrowder của: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy Ngày

Cảm ơn, vì tôi cũng cần sắp xếp các khóa đối tượng!
scottwernervt

4

Ngoài ra, bạn có thể sử dụng phương thức lodash toPairs :

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));

1

ES6

chuyển đổi đối tượng thành bản đồ:

const objToMap = (o) => new Map(Object.entries(o));

chuyển đổi bản đồ thành đối tượng:

const mapToObj = (m) => [...m].reduce( (o,v)=>{ o[v[0]] = v[1]; return o; },{} )

Lưu ý: hàm mapToObj giả định các khóa bản đồ là chuỗi (nếu không sẽ bị lỗi)


-1

Với sự trợ giúp của một số JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});

4
Vào năm 2019, tôi cảm thấy chúng ta không nên TÌM KIẾM jquery để giải quyết vấn đề.
illcrx

jQuery vẫn còn trong rất nhiều dự án và đây không phải là một câu trả lời tồi, chỉ là không tối ưu.
boatcoder
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.