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

时间:2015-07-03 20:15:35

标签: javascript arrays sorting

假设我有这个数组:

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

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

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

ECMAScript language specifications that tell me that

  

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

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

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

或者

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

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

5 个答案:

答案 0 :(得分:21)

理论上,在排序之前,您可以在数组中跟踪其索引,并在排序时将其考虑在内。

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

答案 1 :(得分:2)

由于数组包含引用类型objet,因此可以在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));

答案 2 :(得分:1)

最近发生了变化。 As of ES2019Array#sort稳定,这意味着具有相同name的两个条目相对于彼此的位置将与数组之前相同被排序:

  

22.1.3.27 Array.prototype.sort(comparefn)

     

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

(我的重点)

因此,在您的情况下,{ name: "background-color", value: "rgb(0, 0, 0)" }{ name: "background-color", value: "rgba(0, 0, 0, .5)" }的顺序将保持不变,因为它们比较相等。

在ES2019之前,排序不是必需稳定的,因此它们可以颠倒顺序-或不取决于实现。

答案 3 :(得分:1)

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

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

此方法的缺点是效率不如合并排序,但它非常接近,因为大多数浏览器实现在内部将Array.prototype.sort用于BubbleSort甚至是最佳方法(在这种情况下,最终应该只是大约是O(2n)+ O(nlogn))。

好处是它非常方便且易于阅读-imho?

答案 4 :(得分:0)

为数组添加额外属性: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 }
];

然后更改排序功能,以便在名称相等的情况下按顺序排序:

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

请注意,订单返回的符号必须根据您是希望按升序还是递减来调整(此处,我假设您要从最大到最小排序,因此请返回较小的一个顺序)。