是否可以对 ES6 映射对象进行排序
Is it possible to sort a ES6 map object?
是否可以对 es6 map 对象的条目进行排序?
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 }
}
根据 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
将正确排序。
简答
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())
}
像这样的错误真的很难调试 - 不要冒险!
如果要对键或值进行排序,最好在排序函数中使用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)))
如果您需要与字符串的字母顺序不同的比较,请不要忘记始终确保返回之前和之后的-1
和1
,而不是像原始a[0] > b[0]
那样false
或0
,因为这被视为相等的。
使用 Array.from
将Map
转换为数组,对数组进行排序,转换回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[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
问候。
这个想法是将映射的键提取到数组中。对此数组进行排序。然后遍历这个排序数组,从未排序的映射中获取它的值对,并将它们放入新映射中。新地图将按排序顺序排列。下面的代码是它的实现:
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);
您可以转换为数组并对其调用数组 soring 方法:
[...map].sort(/* etc */);
2 个小时来了解细节。
请注意,问题的答案已在 https://stackoverflow.com/a/31159284/984471
但是,这个问题有不常见的键,
下面是一个清晰而一般的解释示例,提供了更清晰的说明:
- 这里还有更多例子:https://javascript.info/map-set
- 您可以将以下代码复制粘贴到以下链接,并针对您的特定用例对其进行修改:https://www.jdoodle.com/execute-nodejs-online/
.
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
希望有帮助。
不幸的是,在 ES6 中并没有真正实现。您可以使用ImmutableJS的OrderedMap.sort((或Lodash的_.sortBy((使用此功能。
一种方法是获取条目数组,对其进行排序,然后使用排序数组创建一个新 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);
})
下面的代码片段按键对给定的映射进行排序,并再次将键映射到键值对象。我使用了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'}];
也许是一个更现实的例子,关于不对 Map 对象进行排序,但在执行 Map 之前预先准备排序。如果你这样做,语法实际上会变得非常紧凑。你可以像这样在映射函数之前应用排序,在映射之前使用排序函数(我正在使用JSX语法处理的React应用程序的示例(
标记我在这里使用箭头函数在内部定义了一个排序函数,如果它较小,则返回 -1,否则根据我从 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 转换为数组并以这种方式排序的其他解决方案具有以下错误:
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}
排序会创建一个新对象,并且指向未排序对象的所有其他指针都会损坏。
略有变化 - 我没有传播语法,我想在object
而不是Map
上工作。
Object.fromEntries(Object.entries(apis).sort())
这是通过递减对 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);
- 如何在节点中进行 Json 对象排序.js
- jQuery.每个对象排序
- Javascript对象排序
- Javascript 对象排序
- Javascript中的JSON对象排序
- jQuery停止对象排序
- 带有下划线排序的对象排序的数组
- JS对象排序日期排序
- JSON对象排序被jQuery或Javascript篡改
- 高级对象排序
- 将json对象排序到嵌套树中
- 按两个不同的条件对数组中的对象排序
- 如何在angular js中使用自定义排序顺序对对象排序
- 在AngularJS的ng-repeat中,使用Underscore/Lodash按值将对象排序为一对数组
- 给定一个对象数组,如何根据深度为n的属性对对象排序
- JavaScript数组对象排序没有按预期工作
- 对数组内的对象排序
- 自定义javascript对象排序和维护索引关联
- Javascript对象排序不工作
- Javascript对象的对象排序