javascript 对数组项进行排序并保留相同元素的顺序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31213696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 13:28:20  来源:igfitidea点击:

Sort array items and preserve order of same elements

javascriptarrayssorting

提问by Salman A

Suppose I have this array:

假设我有这个数组:

var array = [
  { name: "border-color", value: "#CCCCCC" },
  { name: "color", value: "#FFFFFF" },
  { name: "background-color", value: "rgb(0, 0, 0)" },
  { name: "background-color", value: "rgba(0, 0, 0, .5)" }
];

And this function to sort the array by name:

这个函数按名称对数组进行排序:

array.sort(function(a, b) {
  if (a.name < b.name) return -1;
  if (a.name > b.name) return 1;
  return 0;
});

And ECMAScript language specifications that tell me that:

ECMAScript语言规范,跟我说

The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order).

排序不一定稳定(即比较相等的元素不一定保持其原始顺序)。

So, after sorting, the two items with name = background color could appear in any order i.e.:

因此,排序后,名称 = 背景颜色的两个项目可以以任何顺序出现,即:

[
  { name: "background-color", value: "rgb(0, 0, 0)" },
  { name: "background-color", value: "rgba(0, 0, 0, .5)" },
  ...
]

Or

或者

[
  { name: "background-color", value: "rgba(0, 0, 0, .5)" },
  { name: "background-color", value: "rgb(0, 0, 0)" },
  ...
]

How can I sort the array so that items with same name maintain their relative order? I would rather not hardcode anything.

如何对数组进行排序,以便具有相同名称的项目保持其相对顺序?我宁愿不硬编码任何东西。

回答by noveyak

Theoretically before sorting you could keep track of its index in the array and take that into account when sorting.

理论上,在排序之前,您可以跟踪它在数组中的索引,并在排序时考虑到这一点。

var sortArray = yourarray.map(function(data, idx){
    return {idx:idx, data:data}
})

sortArray.sort(function(a, b) {
  if (a.data.name < b.data.name) return -1;
  if (a.data.name > b.data.name) return 1;
  return a.idx - b.idx
});

var answer = sortArray.map(function(val){
    return val.data
});

回答by T.J. Crowder

This changed recently. As of ES2019, Array#sortis stable, meaning that the two entries with the same namewill have the same position relative to one-another as they did before the array was sorted:

这最近发生了变化。从 ES2019 开始Array#sortstable,这意味着具有相同的两个条目name将具有与数组排序之前相同的相对位置:

22.1.3.27Array.prototype.sort ( comparefn )

The elements of this array are sorted. The sort must be stable(that is, elements that compare equal must remain in their original order).

22.1.3.27Array.prototype.sort ( comparefn )

此数组的元素已排序。排序必须是稳定的(也就是说,比较相等的元素必须保持其原始顺序)。

(my emphasis)

(我的重点)

So in your case, the order of { name: "background-color", value: "rgb(0, 0, 0)" }and { name: "background-color", value: "rgba(0, 0, 0, .5)" }will remain the same, because they compare as equal.

所以你的情况,顺序{ name: "background-color", value: "rgb(0, 0, 0)" }{ name: "background-color", value: "rgba(0, 0, 0, .5)" }将保持不变,因为他们比较结果为相等。

Prior to ES2019, the sort wasn't required to be stable, so they could have had their order reversed — or not, depending on the implementation.

在 ES2019 之前,排序不需要是稳定的,所以他们可以颠倒顺序——或者不颠倒,这取决于实现。

回答by rob2d

In ES6, if you cannot guarantee the sort order because of the browser environment or transpiler plugin set-up (in an ideal world, this shouldn't be a problem), a very idiomatic/quick approach could be:

在 ES6 中,如果由于浏览器环境或转译器插件设置而无法保证排序顺序(在理想情况下,这应该不是问题),一个非常惯用/快速的方法可能是:

values
 .map((v, i) => ([v,i]))
 .sort(([v1,i1], [v2,i2])=> (
     (v1==v2) ? (i2-i1) : (v2-v1)
)).map(([v,i])=>v); // or above flipped

The downside of this is it isn't quite as efficient as a merge sort, but it's pretty close since most browser implementations use it for Array.prototype.sortinternally or even more optimal (and in this case should end up just being about O(2n) + O(nlogn)).

这样做的缺点是它不如合并排序那么有效,但它非常接近,因为大多数浏览器实现将它用于Array.prototype.sort内部或更优化(在这种情况下最终应该只是 O(2n) + O (nlogn))。

The upside is it's very convenient and easy to read -- imho

好处是它非常方便且易于阅读——恕我直言

回答by Emad Y

Add an extra attribute to the array: order

给数组添加一个额外的属性:order

var array = [
    { name: "border-color", value: "#CCCCCC", order: 1 },
    { name: "color", value: "#FFFFFF", order: 2 },
    { name: "background-color", value: "rgb(0, 0, 0)", order: 3 },
    { name: "background-color", value: "rgba(0, 0, 0, .5)", order: 4 }
];

and then change the sort function to sort on order if the name is equal:

如果名称相等,然后将排序函数更改为按顺序排序:

array.sort(function(a, b) {
    if (a.name < b.name) return -1;
    if (a.name > b.name) return 1;
    if (a.name == b.name) {
        if(a.order > b.order) return -1; else return 1;
    }
});

Note that the sign of the return for the order has to be tweaked depending on whether you want it sorted increasing or decreasing (here, I assumed you're sorting from largest to smallest, so return the one with the smaller order).

请注意,必须根据您希望它是按递增还是递减排序来调整订单的返回符号(在这里,我假设您是从最大到最小排序,因此返回具有较小顺序的那个)。

回答by Psddp

Since the array contains reference type objet, the index of the element can be determined in the sort function. So we avoid the map before and after the sort.

由于数组包含引用类型对象,因此可以在排序函数中确定元素的索引。所以我们避免了排序前后的映射。

sortArray.sort((function(a, b) {
  if (a.name < b.name) return -1;
  if (a.name > b.name) return 1;
  return this.indexOf(a) - this.indexOf(b);
}).bind(sortArray));