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 2015

1
マップ上で反復する(つまり、配列に変換する)ときにエントリを並べ替えます
Halcyon 2015

回答:


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()て、配列は各要素の文字列変換に従って、各文字のUnicodeコードポイント値に従ってソートされることに注意してください。したがって、2-1, 0-1, 3-1正しくソートされます。


7
そのfunction(a、b)を次のように短縮できます:var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));矢印関数(ラムダ)の使用
Jimmy Chandra 2015

2
実際には、それがacutally字句比較2-1,foo0-1,bar3-1,baz
Bergi


3
...ドットがそうでなければ、MapIterator並べ替えしようとしているが重要です
muttonUp

2
マップキーが数字の場合、ソートされたマップの1e-9100に置かれるような数字で無効な結果が得られます。:数字で動作するコード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を返すequals条件を持つソート関数を渡します。

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

等しくすることはできない(同一の文字列キーは互いに上書きする)キー文字列を比較すると、等しい条件をスキップできます。ただし、次の場合、レイジー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()はうまく
いき

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 2017

5

配列に変換して、配列のsoringメソッドを呼び出すことができます。

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

3
そして何?マップやオブジェクトではなく配列を取得します
グリーン

5
そしてあなたは鍵を失います
2017


3

1つの方法は、エントリ配列を取得して並べ替えてから、並べ替えられた配列を使用して新しいマップを作成することです。

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

以下のスニペットは、指定されたマップをキーで並べ替え、キーをKey-Valueオブジェクトに再度マップします。マップが文字列->文字列オブジェクトマップであったため、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
これは著者の質問に答えるかもしれませんが、説明する言葉やドキュメントへのリンクが不足しています。生のコードスニペットは、その周りにいくつかのフレーズがないとあまり役に立ちません。回答を編集してください。
こんにちは2018

2
また、これは3年前の質問です。あなたの答えは、他の答えがまだカバーしていないことを提供していると思いますか?
こんにちは2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.