#javascript #arrays #sorting #object
#javascript #массивы #сортировка #объект
Вопрос:
У меня есть массив объектов. Я хочу отсортировать его, оставив некоторые элементы в том же положении (с b =»NOT»)
var a=[{a:1,b:"YES"},{a:2,b:"YES"},{a:5,b:"NOT"},{a:0,b:"NOT"},{a:0,b:"YES"}]
function sortc(x,y){
if (x.b=="NOT" || y.b=="NOT")
return Infinity ;
return (Number(x.a)-Number(y.a))
}
console.log(a.sort(sortc));
результат :
0: {a: 1, b: "YES"}
1: {a: 2, b: "YES"}
2: {a: 5, b: "NOT"}
3: {a: 0, b: "NOT"}
4: {a: 0, b: "YES"}
Ожидаемый результат был (с компонентами сортировки с b =»YES».) :
{ "a": 0, "b": "YES" }
{ "a": 1, "b": "YES" }
{ "a": 5, "b": "NOT" }
{ "a": 0, "b": "NOT" }
{ "a": 2, "b": "YES" }
Комментарии:
1. » Я хочу отсортировать его, оставив некоторые элементы в том же положении (с b =»NOT») » возврат
Infinite
на самом деле не оставляет их в том же месте. Просто означает, чтоy
это должно быть отсортировано послеx
.2.
Number(x.a)
не нужно, посколькуx.a
это ужеnumber
значение.3. Можете ли вы включить желаемый результат?
4. @VLAZ Раздел «результат», по-видимому, является неправильным результатом, а не желаемым.
5. Хм, я ответил до того, как заметил, что это было закрыто. Я думаю, это было ясно для меня, но я согласен, что желаемый результат должен быть включен. Я предполагаю, что вы ожидаете
{ "a": 0, "b": "YES" }, { "a": 1, "b": "YES" }, { "a": 5, "b": "NOT" }, { "a": 0, "b": "NOT" }, { "a": 2, "b": "YES" }
, но я могу ошибаться. Не могли бы вы уточнить? Я бы с удовольствием проголосовал за повторное открытие, когда это произойдет. При необходимости я бы изменил свой ответ.
Ответ №1:
Вы не можете сортировать только некоторые элементы с помощью Array#sort()
метода — вы либо сортируете все, либо ничего. Вы также не определяете положение элементов — вам нужно только определить их связь с другими элементами, а алгоритм сортировки позаботится обо всем остальном.
Что вы можете сделать в качестве обходного пути, так это
- Извлеките все элементы, которые должны быть отсортированы.
- Сортируйте их.
- Перейдите к исходному массиву и замените только то, что должно быть отсортировано, остальные элементы оставьте на своих местах.
var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]
//get only `b: "YES"` items
const dataToSort = a.filter(item => item.b === "YES");
//sort them
dataToSort.sort((x, y) => x.a - y.a);
//replace only items that need to be sorted
const it = dataToSort.values()
for (let i = 0; i < a.length; i ) {
if (a[i].b === "NOT")
continue;
a[i] = it.next().value;
}
console.log(a);
Для записи последний цикл можно просто заменить еще более коротким с большим использованием итератора, хотя это может быть немного более запутанным:
const it = dataToSort.values()
for (const [key, item] of a.entries()) { //use the key-value iterator from the array
if (item.b === "NOT")
continue;
[a[key]] = it; //array destructuring internally advances an iterator
}
var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]
//get only `b: "YES"` items
const dataToSort = a.filter(item => item.b === "YES");
//sort them
dataToSort.sort((x, y) => x.a - y.a);
//replace only items that need to be sorted
const it = dataToSort.values()
for (const [key, item] of a.entries()) {
if (item.b === "NOT")
continue;
[a[key]] = it;
}
console.log(a);
Наконец, это можно сделать несколько более удобным с помощью вспомогательной функции генератора и нескольких небольших служебных функций
/* library code */
const transformArg = transform => f => (...args) => f(transform(...args));
function* filter(predicate, it) {
for (const item of it) {
if (predicate(item))
yield item;
}
}
/* /library code */
var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]
/* helpers */
//extract the `b` key in this case so we don't need to repeat it.
const getSortableAttribute = transformArg(({b}) => b);
//get the value from key-value pair
const getValue = transformArg(([, value]) => value);
//check if the attribute is "YES"
const isSortable = getSortableAttribute(attr => attr === "YES");
const dataToSort = a.filter(isSortable);
dataToSort.sort((x, y) => x.a - y.a);
const it = dataToSort.values()
//iterate only over sortable key-value pairs by re-using the `isSortable` filter
for (const [key, item] of filter(getValue(isSortable), a.entries())) {
[a[key]] = it;
}
console.log(a);
Комментарии:
1. Хорошее использование
.values()
итератора. Вы также можете использоватьa[i] = dataToSort.shift()
, если это слишком запутанно для OP2. Я не фанат этого, поскольку он изменяет массив, а затем перемещает все индексы вниз. Для достаточно больших массивов это может быть проблемой с производительностью. Хотя
.reverse()
.pop()
частично помогает.3. reverse pop быстрее, чем shift?
4. Или
sort
в порядке убывания, а затемpop
🙂5. Я беспокоюсь не о мутации, а об изменении всех индексов. Каждый
.shift()
O(n)
из них должен перемещать все элементы вниз по индексу в массиве. Если нет исключений, о которых я не знаю, я ожидаю, что это произойдет. Итак, три.shift()
операции над массивом из 10 элементов должны будут переместить 9, затем 8, затем 7 элементов вниз по слоту. Reverse — это singleO(n)
и.pop()
thenO(1)
, так что в целом лучше.
Ответ №2:
Это подход sort
, основанный на прямом использовании, но формирующий доступ с Proxy
помощью for length
и индексов.
const
sortOnly = (array, indices) => new Proxy(array, {
get (target, prop) {
if (isFinite(prop)) return target[indices[prop]];
if (prop === 'length') return indices.length;
return target[prop];
},
set (target, prop, receiver) {
target[indices[prop]] = receiver;
return true;
}
}),
array = [{ a: 1, b: "YES" }, { a: 2, b: "YES" }, { a: 5, b: "NOT" }, { a: 0, b: "NOT" }, { a: 0, b: "YES" }];
sortOnly(array, [...array.keys()].filter(i => array[i].b !== 'NOT'))
.sort((a, b) => a.a - b.a)
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }