Подтвердить что ты не робот

Сортировка элементов массива и сохранение порядка одних и тех же элементов

Предположим, что у меня есть этот массив:

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, которые говорят мне, что:

Сорт не обязательно стабильный (то есть элементы, которые сравнивают равные, не обязательно остаются в их первоначальном порядке).

Итак, после сортировки два элемента с именем = цвет фона могут отображаться в любом порядке, например:

[
  { 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)" },
  ...
]

Как я могу отсортировать массив так, чтобы элементы с одинаковым именем сохраняли свой относительный порядок? Я бы предпочел ничего не делать.

4b9b3361

Ответ 1

Теоретически перед сортировкой вы можете отслеживать свой индекс в массиве и учитывать это при сортировке.

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

Ответ 2

Поскольку массив содержит ссылочный тип objet, индекс элемента может быть определен в функции сортировки. Поэтому мы избегаем карты до и после сортировки.

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

Ответ 3

Добавьте дополнительный атрибут в массив: 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;
    }
});

Обратите внимание, что знак возврата для заказа должен быть изменен в зависимости от того, хотите ли вы его сортировать, увеличивая или уменьшая (здесь я предположил, что вы сортируете от самого большого к самому маленькому, поэтому возвращайте один с меньшим порядком).

Ответ 4

Это изменилось недавно. Начиная с ES2019, Array#sort стабильна, это означает, что две записи с одинаковыми name будут иметь одинаковую позицию относительно друг друга, как они делали до сортировки массива:

22.1.3.27 Array.prototype.sort (сравните)

Элементы этого массива отсортированы. Сортировка должна быть стабильной (то есть элементы, которые сравниваются равными, должны оставаться в своем первоначальном порядке).

(мой акцент)

Так что в вашем случае, порядок { name: "background-color", value: "rgb(0, 0, 0)" } и { name: "background-color", value: "rgba(0, 0, 0,.5)" } останется прежним, потому что они сравниваются как равные.

До ES2019 сортировка не требовала стабильности, поэтому их порядок мог быть изменен или нет, в зависимости от реализации.