是否可以对 ES6 地图对象进行排序?
Posted
技术标签:
【中文标题】是否可以对 ES6 地图对象进行排序?【英文标题】:Is it possible to sort a ES6 map object? 【发布时间】:2015-09-18 11:13:28 【问题描述】:是否可以对 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
【问题讨论】:
地图本质上是无序的(除了迭代插入顺序,它需要排序然后[重新]添加)。 在遍历地图时对条目进行排序(即把它变成一个数组)map = new Map([...map].sort())
或 map = new Map([...map].sort((a,b)=>a-b))
【参考方案1】:
根据 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
会被正确排序。
【讨论】:
您可以使用箭头函数 (lambda) 将该函数 (a,b) 内容缩短为:var mapAsc = new Map([...map.entries()].sort((a,b) => a[0] > b[0]));
实际上,它在词法上将2-1,foo
与0-1,bar
和3-1,baz
进行比较
@JimmyChandra: Don't use (a,b) => a[0] > b[0]
!
...
点很重要,否则您正在尝试对 MapIterator 进行排序
如果映射键是数字,你会得到无效的结果,像1e-9
这样的数字在排序后的映射中放在100
之后。适用于数字的代码:new Map([...map.entries()].sort((e1, e2) => e1[0] - e2[0]))
【参考方案2】:
简答
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;对于字符串,the recommended way 正在使用 .localeCompare()
,它可以正确执行此操作并自动处理像 ä
这样位置因用户区域而异的尴尬字符。
所以这里有一个简单的方法来通过字符串keys对地图进行排序:
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
之前和之后,而不是 false
或 0
与原始 @ 987654350@,因为这被视为平等。
【讨论】:
多么好的答案,我们必须修复 S.O.发现机制。这么好的东西不应该丢在这里。【参考方案3】:使用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.values()].sort()
对我不起作用,但 Array.from(map.values()).sort()
对我有用
这实际上帮助了我,没有 Array.from,Typescript 给了我一个编译错误。【参考方案4】:
这个想法是将地图的键提取到一个数组中。对该数组进行排序。然后遍历这个排序数组,从未排序的映射中获取它的值对并将它们放入一个新映射中。新地图将按排序顺序排列。下面的代码是它的实现:
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);
【讨论】:
可以通过unsortedMap.keys()
简单地确定未排序的键。 keys.sort().map...
也应该是 keys.sort().forEach...
。【参考方案5】:
您可以转换为数组并在其上调用数组排序方法:
[...map].sort(/* etc */);
【讨论】:
然后呢?你得到一个数组而不是地图或对象 你丢失了钥匙【参考方案6】:不幸的是,没有真正在 ES6 中实现。您可以通过 ImmutableJS 的 OrderedMap.sort() 或 Lodash 的 _.sortBy() 获得此功能。
【讨论】:
【参考方案7】:一种方法是获取条目数组,对其进行排序,然后使用排序后的数组创建一个新 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】:下面的 sn-p 按其键对给定的映射进行排序,并再次将键映射到键值对象。我使用了 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'];
【讨论】:
【参考方案9】:花了 2 小时了解细节。
注意问题的答案已经在https://***.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
希望对您有所帮助。
【讨论】:
【参考方案10】:我建议为您的地图对象使用 自定义迭代器 来实现排序访问,如下所示:
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
问候。
【讨论】:
【参考方案11】:也许是一个更现实的例子,关于不对 Map 对象进行排序,而是在执行 Map 之前预先准备排序。如果你这样做,语法实际上会变得非常紧凑。您可以像这样在 map 函数之前应用排序,在 map 之前使用排序函数(来自我正在使用 JSX 语法的 React 应用程序的示例)
标记我在这里使用箭头函数定义了一个排序函数
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>
)
【讨论】:
【参考方案12】:据我所知,目前无法正确排序地图。
其他将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
排序创建了一个新对象,所有其他指向未排序对象的指针都被破坏了。
【讨论】:
【参考方案13】:略有不同 - 我没有扩展语法,我想使用 object
而不是 Map
。
Object.fromEntries(Object.entries(apis).sort())
【讨论】:
【参考方案14】:除了上面的正确答案:
如果仅在角度模板中需要排序的地图,则可以使用 KeyValuePipe 轻松排序 其中包含默认排序:
<div *ngFor="let item of map | keyvalue">
item.key : item.value
</div>
它按数字或字符串按字母顺序排序。见https://angular.io/api/common/KeyValuePipe
【讨论】:
以上是关于是否可以对 ES6 地图对象进行排序?的主要内容,如果未能解决你的问题,请参考以下文章