是否可以对ES6地图对象进行排序?

时间:2015-07-01 10:34:52

标签: javascript ecmascript-6

是否可以对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 }
}

15 个答案:

答案 0 :(得分:82)

根据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将被正确排序。

答案 1 :(得分:22)

使用Array.fromMap转换为数组,排序数组,转换回Map,例如

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

答案 2 :(得分:10)

简短答案

 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] ? 0 : a[1] > b[1] ? 1 : -1))

比较不能相等的键字符串(相同的字符串键会互相覆盖),我们可以跳过equals条件。但是,我们仍然应该显式返回-1,因为当a[0] > b[0]时,错误地返回惰性a[0] < b[0]会返回false(视为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]',并返回具有新插入顺序的新Map:< / p>

注意:如果在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]显式访问它们,如下所示。请注意,我们应该在返回之前和之后返回-11,而不是像原始false那样返回0a[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] ? 0 : a[1] > b[1] ? 1 : -1

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

答案 3 :(得分: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);

答案 4 :(得分:3)

您可以转换为数组并在其上调用数组存储方法:

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

答案 5 :(得分:2)

不幸的是,并没有在ES6中真正实现。你有来自ImmutableJS的OrderedMap.sort()或来自Lodash的_.sortBy()这个功能。

答案 6 :(得分:1)

下面的代码段通过其键对给定的地图进行排序,并再次将键映射到键值对象。我使用了localeCompare函数,因为我的map是string-&gt; string object map。

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

答案 7 :(得分:1)

一种方法是获取entry数组,对其进行排序,然后使用排序后的数组创建一个新Map:

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

答案 8 :(得分:1)

据我所知,目前无法正确分类地图。

将Map转换为数组并以此方式排序的其他解决方案存在以下错误:

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}

排序会创建一个新对象,而指向未排序对象的所有其他指针都会损坏。

答案 9 :(得分: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

希望有帮助。

答案 10 :(得分:0)

也许是一个更现实的示例,它不对Map对象进行排序,而是在执行Map之前预先准备好排序。如果您这样做,语法实际上会变得非常紧凑。您可以像这样在map函数之前应用排序功能,在map之前应用排序功能(我正在使用JSX语法的React应用示例)

标记为在这里定义一个排序函数,该函数使用箭头函数定义,如果较小则返回-1,否则返回0,否则将对从API获取的数组中Javascript对象的属性进行排序。

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

答案 11 :(得分:0)

轻微变化-我没有扩展语法,我想使用object而不是Map

Object.fromEntries(Object.entries(apis).sort())

答案 12 :(得分:0)

我建议为您的地图对象使用自定义迭代器来实现排序访问,如下所示:

map[Symbol.iterator] = function* () {
    yield* [...map.entries()].sort((a, b) => a[0].localeCompare(b[0]));
}

使用迭代器的优点是它只需声明一次。在地图中添加/删除条目后,地图上的新 for 循环将使用迭代器自动反映此更改。上面大多数答案中显示的排序副本不会,因为它们仅反映地图在某一时间点的状态。

这是使用您的初始情况的完整工作示例。

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

for (let [key, val] of map) {
    console.log(key + ' - ' + val.name);
}
// 2-1 - foo
// 1-0 - bar

map[Symbol.iterator] = function* () {
    yield* [...map.entries()].sort((a, b) => a[0].localeCompare(b[0]));
}

for (let [key, val] of map) {
    console.log(key + ' - ' + val.name);
}
// 1-0 - bar
// 2-1 - foo

map.set('2-0', { name: 'zzz' });

for (let [key, val] of map) {
    console.log(key + ' - ' + val.name);
}
// 1-0 - bar
// 2-0 - zzz
// 2-1 - foo

问候。

答案 13 :(得分:-1)

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

答案 14 :(得分:-1)

除了上面的正确答案:

如果在角度模板中只需要已排序的 Map,则可以使用 KeyValuePipe 轻松对其进行排序 其中包含默认排序:

<div *ngFor="let item of map | keyvalue">
 {{ item.key }}: {{ item.value }}
</div>

它按字母顺序按数字或字符串排序。见https://angular.io/api/common/KeyValuePipe