У меня есть массив массивов:
[
[
"DL"
],
[
"DL",
"ATL"
],
[
"MQ"
],
[
"MQ",
"DFW"
],
[
"WN"
],
[
"WN",
"MDW"
],
[
"EV"
],
[
"EV",
"ATL"
],
[
"EV",
"IAH"
],
[
"WN",
"LAS"
],
[
"MQ",
"ORD"
],
[
"WN",
"BWI"
]
]
Они уже отсортированы по какому-то определенному порядку, но теперь я хочу отсортировать их снова и переместить все элементы с более чем одним элементом в массиве в текущем порядке ниже элемента с одним элементом в массиве. например
[
[
"DL"
],
[
"DL",
"ATL"
],
[
"MQ"
],
[
"MQ",
"DFW"
],
[
"MQ",
"ORD"
],
[
"WN"
],
[
"WN",
"MDW"
],
[
"WN",
"LAS"
],
[
"WN",
"BWI"
]
[
"EV"
],
[
"EV",
"ATL"
],
[
"EV",
"IAH"
],
]
обратите внимание, что последние три пункта переместились вверх:
[
"WN",
"LAS"
],
[
"MQ",
"ORD"
],
[
"WN",
"BWI"
]
быть последними элементами ниже MQ и WN.
БЕЗ потери заказа.
значение:
MQ/ORD был перемещен чуть ниже MQ/DFW.WN/LAS и WN/BWI были перемещены чуть ниже WN/MDW без потери порядка.Любое предложение для простого и элегантного способа сделать это?
БОНУС:
Если возможно, предлагаемый алгоритм должен обрабатывать и обратную логику:
например
[
[
"WN",
"BWI"
],
[
"MQ",
"ORD"
],
[
"WN",
"LAS"
],
[
"EV",
"IAH"
],
[
"EV",
"ATL"
],
[
"WN",
"MDW"
],
[
"MQ",
"DFW"
],
[
"US"
],
[
"US",
"CLT"
],
[
"EV"
],
[
"MQ"
],
[
"AA"
],
[
"AA",
"DFW"
],
[
"DL"
],
[
"DL",
"ATL"
],
[
"WN"
]
]
Где опять же вложить его нужно таким же образом, не теряя порядка "ТОП" элементов (US должен быть выше EV) и всех элементов, вложенных ниже в том же порядке:
[
[
"US"
],
[
"US",
"CLT"
],
[
"EV"
],
[
"EV",
"IAH"
],
[
"EV",
"ATL"
],
[
"MQ"
],
[
"MQ",
"ORD"
],
[
"MQ",
"DFW"
],
[
"AA"
],
[
"AA",
"DFW"
],
[
"DL"
],
[
"DL",
"ATL"
],
[
"WN"
],
[
"WN",
"BWI"
],
[
"WN",
"LAS"
],
[
"WN",
"MDW"
],
]



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Сначала сопоставьте отношения родитель-потомок, а затем преобразуйте его в результирующий массив:
// swapped the first 2 elements to handle a case of a child going before its parent
const arr = [["DL","ATL"], ["DL"],["MQ"],["MQ","DFW"],["WN"],["WN","MDW"],["EV"],["EV","ATL"],["EV","IAH"],["WN","LAS"],["MQ","ORD"],["WN","BWI"]];
const mapped =
arr.reduce((map, item) => {
map[item[0]] ??= [];
item.length > 1 && map[item[0]].push(item);
return map;
}, {});
const result = Object.entries(mapped).reduce((result, [key, val]) => {
result.push([key], ...val);
return result;
}, []);
console.info(JSON.stringify(result));Более элегантным решением было бы использование sort(), но оно намного медленнее.
const arr = [["DL", "ATL"], ["DL"], ["MQ"], ["MQ", "DFW"], ["WN"], ["WN", "MDW"], ["EV"], ["EV", "ATL"], ["EV", "IAH"], ["WN", "LAS"], ["MQ", "ORD"], ["WN", "BWI"]];
arr.sort((a, b) => {
// if items on the same level or have a different parent
if (a.length === b.length || a[0] !== b[0]) {
// sort by parent index
return arr.findIndex(item => item[0] === a[0]) -
arr.findIndex(item => item[0] === b[0]);
}
return a.length - b.length;
});
console.info(JSON.stringify(arr));И сравнение двух с массивом, увеличенным в 500 раз:
<script benchmark data-count = "1">
const original = [["DL", "ATL"], ["DL"], ["MQ"], ["MQ", "DFW"], ["WN"], ["WN", "MDW"], ["EV"], ["EV", "ATL"], ["EV", "IAH"], ["WN", "LAS"], ["MQ", "ORD"], ["WN", "BWI"]];
// increase the input array 500x
const getData = () => Array.from({ length: 500 }, (_, idx) => original.map(item => {
return item.map(name => `${name}${idx}`);
}))
.flat();
// @benchmark mapped solution
const arr = getData();
// @run
const mapped =
arr.reduce((map, item) => {
map[item[0]] ??= [];
item.length > 1 && map[item[0]].push(item);
return map;
}, {});
Object.entries(mapped).reduce((result, [key, val]) => {
result.push([key], ...val);
return result;
}, []);
// @benchmark sort solution
const arr2 = getData();
// @run
arr2.sort((a, b) => {
if (a.length === b.length || a[0] !== b[0]) {
return arr2.findIndex(item => item[0] === a[0]) -
arr2.findIndex(item => item[0] === b[0]);
}
return a.length - b.length;
});
</script>
<script src = "https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>EV элементы должны оставаться последними
@ET-CS понятно, готово
Идеальный!! Спасибо!!
Я бы хотел, чтобы это было более простое решение - но это потрясающе! работающий!
@ET-CS я пытаюсь сделать это с sort(). я сообщу, если я приду к решению.
Несколько плохих практик в этом коде. что es-lint не нравится. например, переназначение параметра внутри сокращения или for..in вместо Object.values()... ничего, что я не могу исправить :)
@ET-CS я не вижу переназначения параметров, я бы хотел, чтобы у меня был ваш .eslintrc.js, исправление)
@ET-CS, не могли бы вы дать мне правило переназначения параметров eslint?
Это нормально. Я разберусь с вопросами estlint :)
Примечание. Этот код предполагает, что одноэлементные массивы всегда будут первыми. Я обновил вопрос бонусной частью в обратном порядке. что является жизнеспособным условием, которое у меня есть в моем приложении. Если у кого-то есть хорошее решение, которое справится с обоими, это будет здорово :)
@ET-CS проверьте это, сделал конкретный случай, когда ребенок идет раньше своего родителя
без переназначения параметров - без неиспользуемых выражений - без ограничений синтаксиса
обратите внимание, что я добавил бонусную часть для обратной логики. есть идеи, как поддерживать оба пути в вашей логике?
@ET-CS я уже реализовал бонусную часть, проверьте пожалуйста
Ты восхитителен!
Одно но :) у вас ошибка в коде. Посмотрите на свою консоль... в ней есть объекты вместо массивов для отдельных элементов (DL вместо ['DL'])
@ET-CS да, исправляю, также у меня есть решение sort()!
arr.sort((a, b) => a.length - b.length).filter(a => a.length === 1).flatMap(a => arr.filter(b => b[0] === a[0]))@Thomas не справляется с ребенком, идущим раньше своего родителя
@AlexanderNenashev да, для этого и нужен sort().
Удивительный! Итак, я вижу, что сортировка менее эффективна, чем карта!
@Thomas ой, я ошибся, это вообще не работает: [["DL"],["MQ"],["WN"],["EV"],["DL","ATL"] ,["MQ","DFW"],["WN","MDW"],["EV","ATL"],["EV","IAH"],["WN", "LAS"],["MQ","ORD"],["WN","BWI"]]
@ET-CS обновил бенчмарк и кажется, что сортировка НАМНОГО медленнее...
Спасибо! Это не то решение, о котором меня просили :) — оно сортирует ВСЕ элементы в алфавитном порядке, но мне нужно сохранить порядок и размещать элементы только по первому элементу, как в моем примере.