Сортировка массива только выбранных элементов

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

Что-то на подобии этого вопроса.

И так имеется массив 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 шт):

Автор решения: stylok

Дабы упростить вашу жизнь :) давайте посмотрим на последовательность действий и оттолкнемся от неё. Не стану писать за вас весь код, и уж тем более перетаскивание элементов но подскажу идеальное решение согласно постановке задачи.

  1. Вы делаете выборку по списку 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 );

  1. вы не поверите, но почти всё готово для упрощения. Всё что нам осталось это вывести элементы в 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>

  1. Осталась лишь самая малость — на стадии 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 );

→ Ссылка
Автор решения: stylok

Поскольку речь зашла о реализации близкой к реальности, то отдельным комментарием:

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>

→ Ссылка