通过对比新旧数组中各元素的前后邻居关系,可精准定位被拖拽移动的元素;当仅两个相邻元素互换位置时,任选其一作为结果即可。
在实现拖拽排序(如 HTML 列表项重排)功能时,一个常见需求是:仅根据拖拽前后的数组状态,快速识别出哪个元素被主动移动了位置。由于用户只拖动一个元素(其余元素相对顺序不变),该元素在新数组中的“上下文”——即其前驱与后继元素——必然发生变化,而其他元素至少保留一个邻居未变。
核心思路是:对每个元素,记录它在旧数组和新数组中的直接邻居(前一项和后一项的 id),然后找出那些在新旧数组中“两个邻居都不同”的元素。这类元素极大概率就是被拖动的目标。
以下是具体实现:
const oldArr = [{id: 'id1'}, {id: 'id2'}, {id: 'id3'}, {id: 'id4'}, {id: 'id5'}, {id: 'id6'}];
const newArr = [{id: 'id1'}, {id: 'id2'}, {id: 'id5'}, {id: 'id3'}, {id: 'id4'}, {id: 'id6'}];
// 构建旧数组中每个 id 对应的邻居数组 [prevId, nextId],边界用 '-' 占位
const oldNeighbors = oldArr.reduce((map, {id}, i, arr) => {
const prev = i === 0 ? '-' : arr[i - 1].id;
const next = i === arr.length - 1 ? '-' : arr[i + 1].id;
map[id] = [prev, next];
return map;
}, {});
// 同理构建新数组的邻居映射
const newNeighbors = newArr.reduce((map, {id}, i, arr) => {
const prev = i === 0 ? '-' : arr[i - 1].id;
const next = i === arr.length - 1 ? '-' : arr[i + 1].id;
map[id] = [prev, next];
return map;
}, {});
// 查找「旧邻居全部未出现在新邻居中」的元素(即两个邻居都变了)
const movedItems = oldArr.filter(({id}) => {
const oldN = oldNeighbors[id];
const newN = newNeighbors[id];
// 若旧前驱和旧后继均不在新邻居中,则判定为移动项
return !newN.includes(oldN[0]) && !newN.includes(oldN[1]);
});
console.log('Moved item:', movedItems); // [{id: 'id5'}]
✅ 优势说明: