Có thể sắp xếp một đối tượng bản đồ ES6 không?


97

Có thể sắp xếp các mục nhập của một đối tượng bản đồ es6 không?

var map = new Map();
map.set('2-1', foo);
map.set('0-1', bar);

kết quả trong:

map.entries = {
    0: {"2-1", foo },
    1: {"0-1", bar }
}

Có thể sắp xếp các mục nhập dựa trên các khóa của chúng không?

map.entries = {
    0: {"0-1", bar },
    1: {"2-1", foo }
}

4
Bản đồ vốn dĩ không có thứ tự (ngoại trừ thứ tự chèn được lặp lại, yêu cầu sắp xếp và sau đó [lại] thêm).
user2864740

1
Sắp xếp các mục nhập khi bạn lặp lại trên bản đồ (tức là biến nó thành một mảng)
Halcyon

Câu trả lời:


139

Theo tài liệu MDN:

Một đối tượng Bản đồ lặp lại các phần tử của nó theo thứ tự chèn.

Bạn có thể làm theo cách này:

var map = new Map();
map.set('2-1', "foo");
map.set('0-1', "bar");
map.set('3-1', "baz");

var mapAsc = new Map([...map.entries()].sort());

console.log(mapAsc)

Sử dụng .sort(), hãy nhớ rằng mảng được sắp xếp theo giá trị điểm mã Unicode của mỗi ký tự, theo sự chuyển đổi chuỗi của mỗi phần tử. Vì vậy 2-1, 0-1, 3-1sẽ được sắp xếp chính xác.


7
bạn có thể rút ngắn mà function (a, b) chất liệu để: var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));sử dụng chức năng mũi tên (lambda)
Jimmy Chandra

2
Trong thực tế, nó acutally lexically so sánh 2-1,foođể 0-1,bar3-1,baz
Bergi


3
Các ...dấu chấm được quan trọng nếu không bạn đang cố gắng sắp xếp một MapIterator
muttonUp

2
Nếu các khóa bản đồ là số, bạn sẽ nhận được kết quả không hợp lệ với các số như 1e-9được đặt sau 100trong bản đồ được sắp xếp. Mã hoạt động với các số:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr

58

Câu trả lời ngắn

 new Map([...map].sort((a, b) => 
   // Some sort function comparing keys with a[0] b[0] or values with a[1] b[1]
   // Be sure to return -1 if lower and, if comparing values, return 0 if equal
 ))

Ví dụ: so sánh các chuỗi giá trị, có thể bằng nhau, chúng tôi truyền một hàm sắp xếp truy cập [1] và có điều kiện bằng trả về 0:

 new Map([...map].sort((a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)))

So sánh các chuỗi khóa, không thể bằng nhau (các khóa chuỗi giống nhau sẽ ghi đè lên nhau), chúng ta có thể bỏ qua điều kiện bằng. Tuy nhiên, chúng ta vẫn nên trả về -1 một cách rõ ràng, bởi vì trả về một lười biếng a[0] > b[0]không chính xác sẽ cho sai (được coi là 0, tức là bằng) khi a[0] < b[0]:

 new Map([...map].sort((a, b) => a[0] > b[0] ? 1 : -1))

Chi tiết với các ví dụ

Các .entries()trong [...map.entries()](đề xuất trong nhiều câu trả lời) là không cần thiết, có thể thêm một lần lặp thêm bản đồ trừ khi sử dụng tối ưu động cơ JS mà đi cho bạn.

Trong trường hợp thử nghiệm đơn giản, bạn có thể thực hiện những gì câu hỏi yêu cầu với:

new Map([...map].sort())

... mà, nếu các khóa là tất cả các chuỗi, so sánh các chuỗi khóa-giá trị được nối bằng dấu phẩy bị thu hẹp và ép buộc như '2-1,foo''0-1,[object Object]', trả về một Bản đồ mới với thứ tự chèn mới:

Lưu ý: nếu bạn chỉ thấy {}trong đầu ra bảng điều khiển của SO, hãy tìm trong bảng điều khiển trình duyệt thực của bạn

const map = new Map([
  ['2-1', 'foo'],
  ['0-1', { bar: 'bar' }],
  ['3-5', () => 'fuz'],
  ['3-2', [ 'baz' ]]
])

console.log(new Map([...map].sort()))

TUY NHIÊN , nó không phải là một phương pháp hay nếu dựa vào sự ép buộc và nghiêm ngặt như thế này. Bạn có thể nhận được những điều bất ngờ như:

const map = new Map([
  ['2', '3,buh?'],
  ['2,1', 'foo'],
  ['0,1', { bar: 'bar' }],
  ['3,5', () => 'fuz'],
  ['3,2', [ 'baz' ]],
])

// Compares '2,3,buh?' with '2,1,foo'
// Therefore sorts ['2', '3,buh?'] ******AFTER****** ['2,1', 'foo']
console.log('Buh?', new Map([...map].sort()))

// Let's see exactly what each iteration is using as its comparator
for (const iteration of map) {
  console.log(iteration.toString())
}

Những lỗi như thế này thực sự rất khó để gỡ lỗi - đừng mạo hiểm!

Nếu bạn muốn sắp xếp trên các khóa hoặc giá trị, tốt nhất nên truy cập chúng một cách rõ ràng bằng a[0]b[0]trong hàm sắp xếp, như thế này. Lưu ý rằng chúng ta nên quay lại -11trước và sau, không falsehoặc 0như với dữ liệu thô a[0] > b[0]vì điều đó được coi là bằng:

const map = new Map([
  ['2,1', 'this is overwritten'],
  ['2,1', '0,1'],
  ['0,1', '2,1'],
  ['2,2', '3,5'],
  ['3,5', '2,1'],
  ['2', ',9,9']
])

// For keys, we don't need an equals case, because identical keys overwrite 
const sortStringKeys = (a, b) => a[0] > b[0] ? 1 : -1 

// For values, we do need an equals case
const sortStringValues = (a, b) => (a[1] > b[1] && 1) || (a[1] === b[1] ? 0 : -1)

console.log('By keys:', new Map([...map].sort(sortStringKeys)))
console.log('By values:', new Map([...map].sort(sortStringValues)))


11
Thật là một câu trả lời, chúng ta phải sửa các cơ chế khám phá SO. Thứ tốt này không nên bị mất ở đây.
serraosays

30

Chuyển đổi Mapthành một mảng bằng cách sử dụng Array.from, sắp xếp mảng, chuyển đổi trở lại Map, ví dụ:

new Map(
  Array
    .from(eventsByDate)
    .sort((a, b) => {
      // a[0], b[0] is the key of the map
      return a[0] - b[0];
    })
)

1
[...map.values()].sort()không làm việc cho tôi, nhưng Array.from(map.values()).sort()đã làm
Rob Juurlink

1
Điều này thực sự đã giúp tôi, nếu không có Array.from, Typescript đã cho tôi một lỗi biên dịch.
icSapper

7

Ý tưởng là trích xuất các khóa trên bản đồ của bạn thành một mảng. Sắp xếp mảng này. Sau đó, lặp lại mảng đã sắp xếp này, lấy cặp giá trị của nó từ bản đồ chưa được sắp xếp và đưa chúng vào một bản đồ mới. Bản đồ mới sẽ được sắp xếp theo thứ tự. Đoạn mã dưới đây là sự triển khai của nó:

var unsortedMap = new Map();
unsortedMap.set('2-1', 'foo');
unsortedMap.set('0-1', 'bar');

// Initialize your keys array
var keys = [];
// Initialize your sorted maps object
var sortedMap = new Map();

// Put keys in Array
unsortedMap.forEach(function callback(value, key, map) {
    keys.push(key);
});

// Sort keys array and go through them to put in and put them in sorted map
keys.sort().map(function(key) {
    sortedMap.set(key, unsortedMap.get(key));
});

// View your sorted map
console.log(sortedMap);

2
Các khóa chưa được sắp xếp có thể được xác định đơn giản bằng cách sử dụng unsortedMap.keys(). Cũng keys.sort().map...nên keys.sort().forEach....
faintsignal

5

Bạn có thể chuyển đổi thành một mảng và gọi các phương thức soring mảng trên đó:

[...map].sort(/* etc */);

3
Vậy thì sao? Bạn nhận được một mảng không phải là bản đồ hoặc đối tượng
Màu xanh lá cây

5
và bạn đánh mất chìa khóa
braks


3

Một cách là lấy mảng mục, sắp xếp nó, rồi tạo một Bản đồ mới với mảng đã sắp xếp:

let ar = [...myMap.entries()];
sortedArray = ar.sort();
sortedMap = new Map(sortedArray);

Nhưng nếu bạn không muốn tạo một đối tượng mới, nhưng làm việc trên cùng một đối tượng, bạn có thể làm như sau:

// Get an array of the keys and sort them
let keys = [...myMap.keys()];
sortedKeys = keys.sort();

sortedKeys.forEach((key)=>{
  // Delete the element and set it again at the end
  const value = this.get(key);
  this.delete(key);
  this.set(key,value);
})

1

Đoạn mã dưới đây sắp xếp bản đồ đã cho theo các khóa của nó và ánh xạ lại các khóa tới các đối tượng khóa-giá trị. Tôi đã sử dụng hàm localeCompare vì bản đồ của tôi là bản đồ đối tượng chuỗi-> chuỗi.

var hash = {'x': 'xx', 't': 'tt', 'y': 'yy'};
Object.keys(hash).sort((a, b) => a.localeCompare(b)).map(function (i) {
            var o = {};
            o[i] = hash[i];
            return o;
        });

kết quả: [{t:'tt'}, {x:'xx'}, {y: 'yy'}];


1

Theo như tôi thấy thì hiện tại không thể sắp xếp Bản đồ đúng cách.

Các giải pháp khác trong đó Bản đồ được chuyển đổi thành một mảng và được sắp xếp theo cách này có lỗi sau:

var a = new Map([[1, 2], [3,4]])
console.log(a);    // a = Map(2) {1 => 2, 3 => 4}

var b = a;
console.log(b);    // b = Map(2) {1 => 2, 3 => 4}

a = new Map();     // this is when the sorting happens
console.log(a, b); // a = Map(0) {}     b = Map(2) {1 => 2, 3 => 4}

Việc sắp xếp tạo ra một đối tượng mới và tất cả các con trỏ khác đến đối tượng chưa được sắp xếp sẽ bị hỏng.


1

Đã dành 2 giờ để tìm hiểu chi tiết.

Lưu ý rằng câu trả lời cho câu hỏi đã được đưa ra tại https://stackoverflow.com/a/31159284/984471

Tuy nhiên, câu hỏi có các phím mà không phải là những người bình thường,
Một ví dụ rõ ràng & chung với lời giải thích, là dưới đây cung cấp một số rõ ràng hơn:

.

let m1 = new Map();

m1.set(6,1); // key 6 is number and type is preserved (can be strings too)
m1.set(10,1);
m1.set(100,1);
m1.set(1,1);
console.log(m1);

// "string" sorted (even if keys are numbers) - default behaviour
let m2 = new Map( [...m1].sort() );
//      ...is destructuring into individual elements
//      then [] will catch elements in an array
//      then sort() sorts the array
//      since Map can take array as parameter to its constructor, a new Map is created
console.log('m2', m2);

// number sorted
let m3 = new Map([...m1].sort((a, b) => {
  if (a[0] > b[0]) return 1;
  if (a[0] == b[0]) return 0;
  if (a[0] < b[0]) return -1;
}));
console.log('m3', m3);

// Output
//    Map { 6 => 1, 10 => 1, 100 => 1, 1 => 1 }
// m2 Map { 1 => 1, 10 => 1, 100 => 1, 6 => 1 }
//           Note:  1,10,100,6  sorted as strings, default.
//           Note:  if the keys were string the sort behavior will be same as this
// m3 Map { 1 => 1, 6 => 1, 10 => 1, 100 => 1 }
//           Note:  1,6,10,100  sorted as number, looks correct for number keys

Hy vọng rằng sẽ giúp.


0

Có lẽ một ví dụ thực tế hơn về việc không sắp xếp đối tượng Bản đồ mà chuẩn bị sắp xếp trước trước khi thực hiện Bản đồ. Cú pháp thực sự khá nhỏ gọn nếu bạn làm như thế này. Bạn có thể áp dụng cách sắp xếp trước hàm bản đồ như thế này, với một hàm sắp xếp trước bản đồ (Ví dụ từ ứng dụng React mà tôi đang làm việc sử dụng cú pháp JSX)

Đánh dấu rằng tôi ở đây xác định một hàm sắp xếp bên trong bằng cách sử dụng một hàm mũi tên trả về -1 nếu nó nhỏ hơn và 0 nếu không được sắp xếp trên thuộc tính của các đối tượng Javascript trong mảng mà tôi nhận được từ một API.

report.ProcedureCodes.sort((a, b) => a.NumericalOrder < b.NumericalOrder ? -1 : 0).map((item, i) =>
                        <TableRow key={i}>

                            <TableCell>{item.Code}</TableCell>
                            <TableCell>{item.Text}</TableCell>
                            {/* <TableCell>{item.NumericalOrder}</TableCell> */}
                        </TableRow>
                    )

-2
let map = new Map();
map.set('2-1', "foo");
map.set('0-1', "bar");
map.set('3-1', "baz");
let mapAsc = new Map([...map.entries()].sort());
console.log(mapAsc);

// Map(3) {"0-1" => "bar", "2-1" => "foo", "3-1" => "baz"}

3
Mặc dù điều này có thể trả lời câu hỏi của các tác giả, nhưng nó thiếu một số từ giải thích và liên kết đến tài liệu. Các đoạn mã thô không hữu ích lắm nếu không có một số cụm từ xung quanh nó. Vui lòng chỉnh sửa câu trả lời của bạn.
hellow

2
Ngoài ra, đây là một câu hỏi 3 năm tuổi. Bạn nghĩ câu trả lời của mình mang lại điều gì mà các câu trả lời khác chưa đề cập đến?
hellow
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.