Сортировка массива только выбранных элементов
Задача заключается в том, чтобы отсортировать массив конкретных элементов, не затрагивая при этом позиции других элементов.
Что-то на подобии этого вопроса.
И так имеется массив storage.
[
0:{ "Match": "fghfghfgh", "UUID": "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
1:{ "Match": "fghfghfghhhhh", "UUID": "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
2:{ "Match": "Попадает под выборку 1", "UUID": "207036a0-48c9-4676-aa21-705a6b7d033a" },
3:{ "Match": "asdasdasdasd", "UUID": "b448c73d-0232-4a6f-9935-7507afd3fb15" },
4:{ "Match": "Попадает под выборку 2", "UUID": "bb9af394-0ab0-44f8-9488-a1ecbb10be75" },
5:{ "Match": "Попадает под выборку 3", "UUID": "ef519c5c-83b8-4fb8-996b-de881a9123dd" },
6:{ "Match": "5555", "UUID": "e089ce64-2774-49b1-ade9-ddc185bf9236" },
7:{ "Match": "Попадает под выборку 4", "UUID": "ba789aab-2eee-4ae9-8fed-d571f76ab2fd" }
]
Каждый элемент имеет уникальное значение UUID, а по Match производится выборка элементов (2,4,5,7) и загрузка их в DOM
<div id="Selections">
<div uuid="207036a0-48c9-4676-aa21-705a6b7d033a"></div>//storage[2] - 1
<div uuid="bb9af394-0ab0-44f8-9488-a1ecbb10be75"></div>//storage[4] - 2
<div uuid="ef519c5c-83b8-4fb8-996b-de881a9123dd"></div>//storage[5] - 3
<div uuid="ba789aab-2eee-4ae9-8fed-d571f76ab2fd"></div>//storage[7] - 4
</div>
После манипуляций с DOM (dragstart/dragover/dragend) положение их поменялось.
<div id="Selections">
<div uuid="207036a0-48c9-4676-aa21-705a6b7d033a"></div>// - 1
<div uuid="ba789aab-2eee-4ae9-8fed-d571f76ab2fd"></div>// - 4
<div uuid="bb9af394-0ab0-44f8-9488-a1ecbb10be75"></div>// - 2
<div uuid="ef519c5c-83b8-4fb8-996b-de881a9123dd"></div>// - 3
</div>
В результате массив должен стать таким:
[
0:{ "Match": "fghfghfgh", "UUID": "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
1:{ "Match": "fghfghfghhhhh", "UUID": "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
2:{ "Match": "Попадает под выборку 1", "UUID": "207036a0-48c9-4676-aa21-705a6b7d033a" },
3:{ "Match": "asdasdasdasd", "UUID": "b448c73d-0232-4a6f-9935-7507afd3fb15" },
4:{ "Match": "Попадает под выборку 4", "UUID": "ba789aab-2eee-4ae9-8fed-d571f76ab2fd" },
5:{ "Match": "Попадает под выборку 2", "UUID": "bb9af394-0ab0-44f8-9488-a1ecbb10be75" },
6:{ "Match": "5555", "UUID": "e089ce64-2774-49b1-ade9-ddc185bf9236" },
7:{ "Match": "Попадает под выборку 3", "UUID": "ef519c5c-83b8-4fb8-996b-de881a9123dd" }
]
Как видно элементы 0,1,3,6, которые не попали в выборку остались на своих местах, а выбранные поменялись местами согласно их новому положению из DOM.
Единственным решением, что пришло в голову:
let uuids = Array.prototype.map.call(Selections.children, i => i.getAttribute("uuid"));// Получаем массив uuid`ов из DOM в соответствии с их положением
let storage = await Storage.getValue("Includes");// Получаем наш массив storage
let newArray = new Array(storage.length);//Создаём новый массив равный storage для сортировки
let i = 0;//Переменная соответствующая положению элемента
storage.forEach((v, k) => {//Перебираем наш массив
if (uuids.includes(v.UUID)) {//Если элемент имеется в нашей выборке
newArray[k] = storage.find(e => e.UUID == uuids[i]);//Ищет элемент в исходном массиве и задаёт ему новую позицию в соответствии сортировки uuids
i++;//Переходим к следующему элементу выборки
} else {
newArray[k] = v;// Оставляем элемент на прежнем месте
}
});
await Storage.setValue("Includes", newArray);//Сохраняем массив
Может кто-то сталкивался с данной задачей, есть ли более деликатное решение?
Ответы (2 шт):
Дабы упростить вашу жизнь :) давайте посмотрим на последовательность действий и оттолкнемся от неё. Не стану писать за вас весь код, и уж тем более перетаскивание элементов но подскажу идеальное решение согласно постановке задачи.
- Вы делаете выборку по списку
UUIDs
и результат вносите в DOM, где при помощиdraggable
вручную меняете их последовательность. Я верно понимаю? Значит всё что вам нужно чтобы упростить задачу — получить не только сами элементы, но и их индекс.
Смотрите.
const storage = [
{ "Match": "fghfghfgh", "UUID": "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
{ "Match": "fghfghfghhhhh", "UUID": "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
{ "Match": "Попадает под выборку 1", "UUID": "207036a0-48c9-4676-aa21-705a6b7d033a" },
{ "Match": "asdasdasdasd", "UUID": "b448c73d-0232-4a6f-9935-7507afd3fb15" },
{ "Match": "Попадает под выборку 2", "UUID": "bb9af394-0ab0-44f8-9488-a1ecbb10be75" },
{ "Match": "Попадает под выборку 3", "UUID": "ef519c5c-83b8-4fb8-996b-de881a9123dd" },
{ "Match": "5555", "UUID": "e089ce64-2774-49b1-ade9-ddc185bf9236" },
{ "Match": "Попадает под выборку 4", "UUID": "ba789aab-2eee-4ae9-8fed-d571f76ab2fd" }
];
const UUIDs = ['207036a0-48c9-4676-aa21-705a6b7d033a', 'bb9af394-0ab0-44f8-9488-a1ecbb10be75', 'ef519c5c-83b8-4fb8-996b-de881a9123dd', 'ba789aab-2eee-4ae9-8fed-d571f76ab2fd'];
const selection = storage.reduce((acc, val, index) => {
if(UUIDs.includes(val.UUID)) acc[index] = val;
return acc
}, {});
console.log( selection );
- вы не поверите, но почти всё готово для упрощения. Всё что нам осталось это вывести элементы в DOM (draggable-контейнер), но с небольшим бонусом в виде атрибута
data
с тем самымindex
;
<div id="Selections">
<div data-index="2" uuid="207036a0-48c9-4676-aa21-705a6b7d033a">storage[2]</div>
<div data-index="4" uuid="bb9af394-0ab0-44f8-9488-a1ecbb10be75">storage[4]</div>
<div data-index="5" uuid="ef519c5c-83b8-4fb8-996b-de881a9123dd">storage[5]</div>
<div data-index="7" uuid="ba789aab-2eee-4ae9-8fed-d571f76ab2fd">storage[7]</div>
</div>
Осталась лишь самая малость — на стадии
drop
получитьdataset.index
тех двух элементов, которые вы только что поменяли местами и внести изменения вstorage
через деструктурирующее присваивание (допустим это были 4 и 7 элементы):[storage[4], storage[7]] = [storage[7], storage[4]];
И вуаля. Впрочем возможно вы захотите пойти пойти по своему пути и вносить сразу несколько изменений,.. деструктурирующее присваивание вам в помощь, если вы знаете их текущий индекс, то можете поменять местами все четыре одновременно.
const storage = [
{ "Match": "fghfghfgh", "UUID": "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
{ "Match": "fghfghfghhhhh", "UUID": "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
{ "Match": "Попадает под выборку 1", "UUID": "207036a0-48c9-4676-aa21-705a6b7d033a" },
{ "Match": "asdasdasdasd", "UUID": "b448c73d-0232-4a6f-9935-7507afd3fb15" },
{ "Match": "Попадает под выборку 2", "UUID": "bb9af394-0ab0-44f8-9488-a1ecbb10be75" },
{ "Match": "Попадает под выборку 3", "UUID": "ef519c5c-83b8-4fb8-996b-de881a9123dd" },
{ "Match": "5555", "UUID": "e089ce64-2774-49b1-ade9-ddc185bf9236" },
{ "Match": "Попадает под выборку 4", "UUID": "ba789aab-2eee-4ae9-8fed-d571f76ab2fd" }
];
[storage[2], storage[4], storage[5], storage[7]] = [storage[2], storage[7], storage[4], storage[5]];
console.log( storage );
Поскольку речь зашла о реализации близкой к реальности, то отдельным комментарием:
let storage = [
{ Match: "fghfghfgh", UUID: "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
{ Match: "fghfghfghhhhh", UUID: "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
{ Match: "Попадает под выборку 1", UUID: "207036a0-48c9-4676-aa21-705a6b7d033a", },
{ Match: "asdasdasdasd", UUID: "b448c73d-0232-4a6f-9935-7507afd3fb15", },
{ Match: "Попадает под выборку 2", UUID: "bb9af394-0ab0-44f8-9488-a1ecbb10be75", },
{ Match: "Попадает под выборку 3", UUID: "ef519c5c-83b8-4fb8-996b-de881a9123dd", },
{ Match: "5555", UUID: "e089ce64-2774-49b1-ade9-ddc185bf9236" },
{ Match: "Попадает под выборку 4", UUID: "ba789aab-2eee-4ae9-8fed-d571f76ab2fd", },
];
const UUIDs = [
'207036a0-48c9-4676-aa21-705a6b7d033a',
'bb9af394-0ab0-44f8-9488-a1ecbb10be75',
'ef519c5c-83b8-4fb8-996b-de881a9123dd',
'ba789aab-2eee-4ae9-8fed-d571f76ab2fd'
];
window.addEventListener("DOMContentLoaded", () => {
const Selections = document.getElementById("Selections");
const selection = storage.reduce((acc, val, index) => {
if(UUIDs.includes(val.UUID)) {
acc[index] = val;
let div = document.createElement("div");
div.textContent = val.Match;
div.dataset.index = index;
div.setAttribute("uuid", val.UUID);
div.setAttribute("draggable", true);
Selections.appendChild(div);
}
return acc;
}, {});
})
function DragAndDrop(id) {
const wrapper = document.getElementById(id);
wrapper.addEventListener('dragstart', (e) => {
this.root = id;
this.target = e.target;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target.outerHTML);
e.target.style.opacity = '0.2';
});
wrapper.addEventListener('dragleave', (e) => {
if (this.root && e.target.draggable) {
e.target.closest('[data-index]')?.classList.remove('over');
}
});
wrapper.addEventListener('dragover', (e) => {
if (this.root) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}
});
wrapper.addEventListener('dragenter', (e) => {
if (this.root) {
e.target.closest('[data-index]')?.classList.add('over');
}
});
wrapper.addEventListener('dragend', (e) => {
delete this.root;
e.target.removeAttribute('style');
});
wrapper.addEventListener('drop', (e) => {
if (this.root && this.target !== e.target && e.target.dataset.index) {
// получаем текущие индексы и меняем из местами в storage
const x = this.target.dataset.index;
const y = e.target.dataset.index;
[storage[x], storage[y]] = [storage[y], storage[x]];
e.target.dataset.index = x; //+ меняем индекс у замещаемого на текущий
e.preventDefault();
e.target.closest('[data-index]')?.classList.remove('over');
delete this.root;
this.target.outerHTML = e.target.outerHTML;
e.target.outerHTML = e.dataTransfer.getData('text/html');
const current = Selections.querySelector(`[uuid="${this.target.getAttribute('uuid')}"]`);
current.dataset.index = y; // меняем индекс у перетаскиваемого на текущий
}
});
}
const all = new DragAndDrop('Selections');
view.addEventListener('click', () => {
console.clear();
console.log('storage', storage);
}, false);
div#Selections>div[index]{
opacity: 0.2;
}
#Selections {
display: flex;
flex-direction: column;
gap: 1px;
}
#Selections div {
background-color: green;
line-height: 32px;
padding: 0 8px;
}
.over {
border: solid 1px black;
}
<div id="Selections"></div>
<br><input type="button" id="view" value="View">
Дополнение согласно реального кода.
Подход к неизвестному числу параметров в присваивании реализован при помощи new Function()
;
let storage = [
{ Match: "fghfghfgh", UUID: "a2e0b517-97d6-4050-bcd1-a0dc43a0b276" },
{ Match: "fghfghfghhhhh", UUID: "57f03e08-9287-49ba-be4a-d59fa9c4e71c" },
{ Match: "Попадает под выборку 1", UUID: "207036a0-48c9-4676-aa21-705a6b7d033a"},
{ Match: "asdasdasdasd", UUID: "b448c73d-0232-4a6f-9935-7507afd3fb15" },
{ Match: "Попадает под выборку 2", UUID: "bb9af394-0ab0-44f8-9488-a1ecbb10be75" },
{ Match: "Попадает под выборку 3", UUID: "ef519c5c-83b8-4fb8-996b-de881a9123dd" },
{ Match: "5555", UUID: "e089ce64-2774-49b1-ade9-ddc185bf9236" },
{ Match: "Попадает под выборку 4", UUID: "ba789aab-2eee-4ae9-8fed-d571f76ab2fd" },
]
const Selections = document.getElementById("Selections");
const UUIDs = [
'207036a0-48c9-4676-aa21-705a6b7d033a',
'bb9af394-0ab0-44f8-9488-a1ecbb10be75',
'ef519c5c-83b8-4fb8-996b-de881a9123dd',
'ba789aab-2eee-4ae9-8fed-d571f76ab2fd'
];
window.addEventListener("DOMContentLoaded", () => {
const selection = storage.reduce((acc, val, index) => {
if(UUIDs.includes(val.UUID)) {
acc[index] = val;
let div = document.createElement("div");
div.textContent = val.Match;
div.dataset.index = index;
div.setAttribute("uuid", val.UUID);
div.setAttribute("draggable", true);
Selections.appendChild(div);
}
return acc;
}, {});
})
const getNextElement = (cursorPosition, currentElement) => {
const currentElementCoord = currentElement.getBoundingClientRect()
const currentElementCenter =
currentElementCoord.y + currentElementCoord.height / 2
const nextElement =
cursorPosition < currentElementCenter
? currentElement
: currentElement.nextElementSibling
return nextElement
}
let rB; // порядок элементов "До".
Selections.addEventListener("dragstart", e => {
let currentElement = e.target;
currentElement.setAttribute("index", Position(currentElement));
// собираем текущий индекс элементов
let range = [...Selections.querySelectorAll('[data-index]')];
rB = range.map(b => b.dataset.index);
// console.log('rB', rB);
});
Selections.addEventListener("dragend", async e => {
let currentElement = e.target;
let index = {}
index.Previous = parseInt(currentElement.getAttribute("index"));
index.Current = Position(currentElement);
currentElement.removeAttribute("index");
if (index.Previous === index.Current) return;
// rA порядок элементов "После".
let range = [...Selections.querySelectorAll('[data-index]')];
const rA = range.map(b => b.dataset.index);
// console.log('rA', rA);
const assignmentString = '['+rB.map(b => `storage[${b}]`)+']=['+rA.map(b => `storage[${b}]`)+']';
const destructuringAssignment = new Function(assignmentString);
destructuringAssignment();
range.map((el,i) => el.dataset.index = rA[i]); // переиндексировать на текущие цифры
console.log('storage', storage);
});
function Position(element) {
return Array.prototype.indexOf.call(Selections.children, element);
}
Selections.addEventListener("dragover", async e => {
e.preventDefault();
const activeElement = Selections.querySelector("div[index]");//Selections.querySelector(`.selected`);
let currentElement = e.target;
if (currentElement?.parentElement?.parentElement == Selections) currentElement = currentElement.parentElement;
//console.log(activeElement, currentElement);
const isMoveable = activeElement !== currentElement;
if (!isMoveable) return;
const nextElement = getNextElement(e.clientY, currentElement);
if (nextElement && activeElement === nextElement.previousElementSibling || activeElement === nextElement) return;
Selections.insertBefore(activeElement, nextElement);
//console.log(activeElement, nextElement);
});
div#Selections>div[index] {
opacity: 0.2;
}
#Selections {
background-color: green
}
<div id="Selections"></div>