Коротко — потому что при сравнении "старого" и "нового" списков простая проверка индексов даёт ложные срабатывания: когда вы вынимаете/вставляете элемент, индексы остальных элементов меняются, и простое сравнение индекса в appliedList и draftList покажет, что они "переместились", хотя их относительный порядок не поменялся.
Что делать — необходимо находить минимальный набор реально перемещённых элементов, а не смотреть на абсолютные индексы. Стандартный приём — сопоставить id элементов (или другой уникальный ключ), построить для draftList массив индексов из appliedList и найти в нём Longest Increasing Subsequence (LIS). Элементы, входящие в LIS, сохранили относительный порядок и фактически не перемещались; остальные — действительно перемещены.
Пример функции на JS, принимающей списки элементов (или id) и возвращающей сообщения о реальных перемещениях:
```js
function getMoveMessages(appliedList, draftList, getId = x => x) {
// карта id -> исходный индекс
const idToIndex = new Map(appliedList.map((item, i) => [getId(item), i]));
// последовательность исходных индексов в порядке draftList; пропускаем новые элементы
const seq = [];
const draftIds = [];
for (const item of draftList) {
const id = getId(item);
const idx = idToIndex.get(id);
if (idx !== undefined) {
seq.push(idx);
draftIds.push(id);
}
}
// LIS (по индексам) — стандартный O(n log n) алгоритм с восстановлением
const n = seq.length;
const tails = []; // значение последнего элемента для каждой длины
const tailsPos = []; // позиция в seq для каждой длины
const prev = new Array(n).fill(-1);
for (let i = 0; i < n; i++) {
const x = seq[i];
// lower_bound по tails
let lo = 0, hi = tails.length;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (tails[mid] < x) lo = mid + 1; else hi = mid;
}
if (lo > 0) prev[i] = tailsPos[lo - 1];
tails[lo] = x;
tailsPos[lo] = i;
}
// восстановим индексы элементов, входящих в LIS
let lisIdx = [];
if (tailsPos.length > 0) {
let p = tailsPos[tailsPos.length - 1];
while (p !== -1) {
lisIdx.push(p);
p = prev[p];
}
lisIdx.reverse();
}
const inLIS = new Set(lisIdx.map(i => draftIds[i]));
// сформируем сообщения: те id, которые есть в appliedList и draftList, но не в LIS
const messages = [];
for (let newIndex = 0; newIndex < draftList.length; newIndex++) {
const id = getId(draftList[newIndex]);
if (!idToIndex.has(id)) {
messages.push(`Добавлен ${id} на позицию ${newIndex}`); // опционально: новые элементы
continue;
}
if (!inLIS.has(id)) {
const oldIndex = idToIndex.get(id);
messages.push(`Элемент ${id} перемещён с ${oldIndex} на ${newIndex}`);
}
}
// опционально: можно также показать удалённые элементы из appliedList, которых нет в draftList
for (const item of appliedList) {
const id = getId(item);
if (!draftList.some(d => getId(d) === id)) {
messages.push(`Удалён ${id}`);
}
}
return messages;
}
```
Пример использования:
- если appliedList = [1,2,3,4,5,6] и draftList = [1,2,3,6,5,4], то LIS даст [1,2,3] (или возможно [1,2,3,5] в зависимости), и будут помечены как перемещённые только те элементы, которые реально изменили относительный порядок (в вашем случае — 6 и 4), а 5 не будет показываться, если его относительный порядок не нарушен.
Итог: причина появления "5" — вы сравнивали абсолютные индексы. Решение — сравнивать относительный порядок и использовать LIS, чтобы отличать реальные перемещения от сдвигов, вызванных удалением/вставкой других элементов. Если пришлёте ваш текущий код (полностью), могу показать, как на нём исправить поведение.