Сортировка элементов массива и сохранение порядка одних и тех же элементов
Предположим, что у меня есть этот массив:
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)" },
...
]
Как я могу отсортировать массив так, чтобы элементы с одинаковым именем сохраняли свой относительный порядок? Я бы предпочел ничего не делать.
Ответы
Ответ 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 сортировка не требовала стабильности, поэтому их порядок мог быть изменен или нет, в зависимости от реализации.