是否可以对 ES6映射对象进行排序?

是否可以对 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 }
}
263905 次浏览

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

不幸的是,在 ES6中并没有真正实现。 ImmutableJS 的 OrderedMap.sort ()或 Lodash 的 _. sortBy ()都有这个特性。

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

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

其思想是将映射的键提取到一个数组中。对这个数组进行排序。然后迭代这个排序的数组,从未排序的映射中获得它的值对,并将它们放入一个新的映射中。新地图将按排序顺序排列。下面的代码是它的实现:

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

使用 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];
})
)

下面的代码片段根据键对给定的 map 进行排序,并再次将键映射到键值对象。我使用 localeCompare 函数,因为我的映射是 string-> string 对象映射。

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

长话短说

 new Map([...map].sort((a, b) =>
// Some sort function comparing keys with a[0] b[0] or values with a[1] b[1]
))

如果你期望字符串: 对于 .sort来说,你需要返回 -1,如果更低,返回0,如果相等; 对于字符串,推荐的方法使用 .localeCompare(),它正确地执行这个操作,并自动处理像 ä这样的笨拙字符,其位置因用户区域而异。

这里有一个按字符串 钥匙对地图进行排序的简单方法:

 new Map([...map].sort((a, b) => String(a[0]).localeCompare(b[0])))

... 通过字符串 价值观:

 new Map([...map].sort((a, b) => String(a[1]).localeCompare(b[1])))

它们是类型安全的,因为它们在命中非字符串键或值时不会抛出错误。开始时的 String()强制 a为字符串(这有利于提高可读性) ,而 .localeCompare()本身强制其参数为字符串而不会出现错误。


举例详细说明

Tldr: ...map.entries()是冗余的,只有 ...map是可以的; 没有传递排序函数的惰性 .sort()存在由字符串强制引起的奇怪的边缘情况错误。

[...map.entries()]中的 .entries()(在许多答案中都有建议)是多余的,可能会增加映射的额外迭代,除非 JS 引擎为您优化了它。

在简单的测试用例中,您可以使用以下命令完成问题所要求的任务:

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

... ... 如果键都是字符串,比较压缩和强制逗号连接的键值字符串,比如 '2-1,foo''0-1,[object Object]',返回一个新的 Map 和新的插入顺序:

注意: 如果在 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())
}

这样的错误真的很难调试-不要冒险!

如果想对键或值进行排序,最好使用如上所示的 sort 函数中的 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']
])


// Examples using array destructuring. We're saying 'keys' and 'values'
// in the function names so it's clear and readable what the intent is.
const sortStringKeys = ([a], [b]) => String(a).localeCompare(b)
const sortStringValues = ([,a], [,b]) => String(a).localeCompare(b)


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

如果你需要一个不同于字符串字母顺序的比较,不要忘记总是确保返回的是之前和之后的 -11,而不是原始的 a[0] > b[0]返回的 false或者 0,因为它们是等价的。

一种方法是获取条目数组,对其进行排序,然后使用排序的数组创建一个新的 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);
})

也许这是一个更现实的例子,不是对 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>
)

据我所知,目前还不可能对 Map 进行正确的排序。

其他将 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}

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

花了两个小时来了解细节。

注意,问题的答案已经在 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

希望能帮上忙。

稍有变化-我没有扩展语法,我想工作的 object而不是 Map

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

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

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

问候。

下面是通过递减对 Map ()进行排序的函数。

function groupBy(list, keyGetter) {
const map = new Map();
list.forEach((item) => {
const key = keyGetter(item);
const collection = map.get(key);
if (!collection) {
map.set(key, [item]);
} else {
collection.push(item);
}
});


const sortedMap = new Map();
[...map].sort((a, b) => b[1].length - a[1].length).forEach(e => sortedMap.set(e[0], e[1]));


return sortedMap;
}
const test = groupBy(array, item => item.fieldName);