ES6지도 객체를 정렬 할 수 있습니까?


98

es6 맵 객체의 항목을 정렬 할 수 있습니까?

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

결과 :

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

키를 기준으로 항목을 정렬 할 수 있습니까?

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

4
맵은 본질적으로 정렬되지 않습니다 (정렬 후 [재] 추가해야하는 반복 삽입 순서 제외).
user2864740

1
맵을 반복 할 때 항목 정렬 (즉, 배열로 변환)
Halcyon

답변:


139

MDN 문서에 따르면 :

Map 객체는 삽입 순서에 따라 요소를 반복합니다.

다음과 같이 할 수 있습니다.

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)

를 사용 .sort()하여 배열은 각 요소의 문자열 변환에 따라 각 문자의 유니 코드 코드 포인트 값에 따라 정렬됩니다. 따라서 2-1, 0-1, 3-1올바르게 정렬됩니다.


7
: 당신에게 물건을 그 기능 (A, B)을 단축 할 수 var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));화살표 함수 (, λ)을 사용하여
지미 찬드라

2
사실, acutally 어휘 비교 2-1,foo0-1,bar3-1,baz
BERGI


3
...점은 중요한 그렇지 않으면 당신은 MapIterator 정렬하려고하는
muttonUp

2
맵 키가 숫자 인 경우 정렬 된 맵 1e-9에 넣는 것과 같은 숫자로 잘못된 결과가 표시 100됩니다. 숫자로 작동하는 코드 :new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
cdalxndr

58

짧은 답변

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

예를 들어, 같을 수있는 값 문자열을 비교하면 [1]에 액세스하고 0을 반환하는 같음 조건이있는 정렬 함수를 전달합니다.

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

같을 수없는 (동일한 문자열 키가 서로 덮어 쓰기) 키 문자열을 비교하면 equals 조건을 건너 뛸 수 있습니다. 그러나 다음과 같은 경우 lazy를 a[0] > b[0]잘못 반환 하면 false (0으로 처리됨, 즉 같음)가 반환되므로 명시 적으로 -1을 반환해야합니다 a[0] < b[0].

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

예제와 함께 자세히

.entries()에서 [...map.entries()](많은 답변에서 제안) 아마 멀리 당신을위한 JS 엔진 최적화합니다 않는지도의 추가 반복을 추가, 중복입니다.

간단한 테스트 케이스에서 질문이 요구하는 것을 다음과 같이 할 수 있습니다.

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

...되는 키 모든 문자열 경우 으깨 콤마 가입 키 값 등 문자열 강제를 비교 '2-1,foo'하고 '0-1,[object Object]'새로운 신청서와 새로운 맵을 반환 :

참고 : {}SO의 콘솔 출력 에만 표시되는 경우 실제 브라우저 콘솔에서 확인하세요.

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

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

그러나 이와 같이 강압과 문자열 화에 의존하는 것은 좋은 습관이 아닙니다. 다음과 같은 놀라움을 얻을 수 있습니다.

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())
}

이와 같은 버그는 디버그하기가 정말 어렵습니다. 위험을 감수하지 마십시오!

당신이 키 또는 값을 정렬 할 경우에 명시 적으로 접근하는 것이 가장 좋습니다 a[0]b[0]같이, 정렬 기능에. 우리가 반환해야 함을 참고 -1하고 1있지, 이전과 이후 false또는 0원시와 같은 a[0] > b[0]이가 동등하게 취급되기 때문에 :

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
정답 이군요. SO 발견 메커니즘을 수정해야합니다. 이 좋은 것을 여기서 잃어 버려서는 안됩니다.
serraosays

30

변환 Map사용하여 배열 Array.from, 정렬 배열로 변환 다시 Map, 예를 들어,

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()나를 위해 작동하지만하지 Array.from(map.values()).sort()않았다
롭 Juurlink에게

1
이것은 실제로 Array.from이 없으면 Typescript가 컴파일 오류를 발생시키는 데 도움이되었습니다.
icSapper

7

아이디어는지도의 키를 배열로 추출하는 것입니다. 이 배열을 정렬하십시오. 그런 다음이 정렬 된 배열을 반복하고 정렬되지 않은 맵에서 값 쌍을 가져 와서 새 맵에 넣습니다. 새지도는 정렬 된 순서로 표시됩니다. 아래 코드는 구현입니다.

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
정렬되지 않은 키는를 사용하여 간단히 확인할 수 있습니다 unsortedMap.keys(). 또한 keys.sort().map...이어야합니다 keys.sort().forEach....
faintsignal

5

배열로 변환하고 배열 소링 메서드를 호출 할 수 있습니다.

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

3
그리고 뭐? 지도 나 객체가 아닌 배열을 얻습니다
Green

5
당신은 키 잃을
braks


3

한 가지 방법은 항목 배열을 가져 와서 정렬 한 다음 정렬 된 배열로 새 맵을 만드는 것입니다.

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

그러나 새 개체를 만들고 싶지 않고 동일한 개체에 대해 작업하려면 다음과 같이 할 수 있습니다.

// 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

아래 스 니펫은 키별로 지정된지도를 정렬하고 키를 키-값 개체에 다시 매핑합니다. 내 맵이 string-> string 객체 맵이기 때문에 localeCompare 함수를 사용했습니다.

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

결과: [{t:'tt'}, {x:'xx'}, {y: 'yy'}];


1

내가보기에 현재지도를 제대로 정렬하는 것은 불가능합니다.

맵이 배열로 변환되고 이런 방식으로 정렬되는 다른 솔루션에는 다음과 같은 버그가 있습니다.

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}

정렬은 새 개체를 만들고 정렬되지 않은 개체에 대한 다른 모든 포인터가 손상됩니다.


1

세부 사항을 파악하는 데 2 ​​시간이 소요되었습니다.

질문에 대한 답변은 이미 https://stackoverflow.com/a/31159284/984471 에서 제공됩니다.

그러나 질문에는 일반적인 것이 아닌 키가 있습니다. 설명이
있는 명확하고 일반적인 예는 다음 과 같이 좀 더 명확성을 제공합니다.

.

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

도움이되기를 바랍니다.


0

아마도 Map 객체를 정렬하지 않고 Map을 수행하기 전에 먼저 정렬을 준비하는보다 현실적인 예일 것입니다. 이렇게하면 구문이 실제로 매우 압축됩니다. 이와 같이 map 함수 앞에 정렬을 적용 할 수 있습니다. map 앞에 sort 함수를 사용하면됩니다 (JSX 구문을 사용하여 작업중인 React 앱의 예).

내가 API에서 가져온 배열의 Javascript 객체 속성에 따라 정렬 된 값이 더 작 으면 -1을 반환하고 그렇지 않으면 0을 반환하는 화살표 함수를 사용하여 내부 정렬 함수를 정의합니다.

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
이것은 저자의 질문에 답할 수 있지만 설명하는 단어와 문서 링크가 부족합니다. 원시 코드 조각은 주변에 일부 문구가 없으면 그다지 도움이되지 않습니다. 답변을 수정하십시오.
hellow

2
또한 이것은 3 년 된 질문입니다. 귀하의 답변은 다른 답변에서 아직 다루지 않은 내용을 제공한다고 생각하십니까?
hellow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.