我很难弄清楚如何移动数组中的一个元素。例如,给定以下条件:
var array = [ 'a', 'b', 'c', 'd', 'e'];
我怎么能写一个函数来移动元素'd'到'b'的左边?
还是c右边的a ?
移动元素之后,应该更新其余元素的索引。结果数组将是:
array = ['a', 'd', 'b', 'c', 'e']
这看起来应该很简单,但我无法理解它。
我很难弄清楚如何移动数组中的一个元素。例如,给定以下条件:
var array = [ 'a', 'b', 'c', 'd', 'e'];
我怎么能写一个函数来移动元素'd'到'b'的左边?
还是c右边的a ?
移动元素之后,应该更新其余元素的索引。结果数组将是:
array = ['a', 'd', 'b', 'c', 'e']
这看起来应该很简单,但我无法理解它。
当前回答
我需要一个不可变的移动方法(一个不改变原始数组的方法),所以我改编了@Reid的接受的答案,简单地使用对象。赋值以在进行拼接之前创建数组的副本。
Array.prototype.immutableMove = function (old_index, new_index) {
var copy = Object.assign([], this);
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) {
copy.push(undefined);
}
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy;
};
下面是一个jsfiddle演示它的运行。
其他回答
下面是我的一行ES6解决方案,带有一个可选参数。
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
this.splice(to, 0, ...this.splice(from, on))
}
}
digiguru提出的第一个解决方案的改编
on的参数是你想要移动的元素个数。
这里有一个可链接的变化:
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
return this.splice(to, 0, ...this.splice(from, on)), this
}
}
[3, 4, 5, 1, 2].move(3, 0, 2) // => [1, 2, 3, 4, 5]
如果你想避免原型污染,这里有一个独立的函数:
function move(array, from, to, on = 1) {
return array.splice(to, 0, ...array.splice(from, on)), array
}
move([3, 4, 5, 1, 2], 3, 0, 2) // => [1, 2, 3, 4, 5]
最后,这是一个不会改变原始数组的纯函数:
function moved(array, from, to, on = 1) {
return array = array.slice(), array.splice(to, 0, ...array.splice(from, on)), array
}
这应该涵盖了其他答案中出现的所有变化。
我需要一个不可变的移动方法(一个不改变原始数组的方法),所以我改编了@Reid的接受的答案,简单地使用对象。赋值以在进行拼接之前创建数组的副本。
Array.prototype.immutableMove = function (old_index, new_index) {
var copy = Object.assign([], this);
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) {
copy.push(undefined);
}
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy;
};
下面是一个jsfiddle演示它的运行。
我已经实现了一个不可变的ECMAScript 6解决方案,基于@Merc的答案在这里:
const moveItemInArrayFromIndexToIndex = (array, fromIndex, toIndex) => {
if (fromIndex === toIndex) return array;
const newArray = [...array];
const target = newArray[fromIndex];
const inc = toIndex < fromIndex ? -1 : 1;
for (let i = fromIndex; i !== toIndex; i += inc) {
newArray[i] = newArray[i + inc];
}
newArray[toIndex] = target;
return newArray;
};
变量名可以缩短,只使用长变量名,这样代码就可以解释自己。
面向对象,可表达,可调试,无突变,已测试。
class Sorter {
sortItem(array, fromIndex, toIndex) {
const reduceItems = () => {
const startingItems = array.slice(0, fromIndex);
const endingItems = array.slice(fromIndex + 1);
return startingItems.concat(endingItems);
}
const addMovingItem = (movingItem, reducedItems) => {
const startingNewItems = reducedItems.slice(0, toIndex);
const endingNewItems = reducedItems.slice(toIndex);
const newItems = startingNewItems.concat([movingItem]).concat(endingNewItems);
return newItems;
}
const movingItem = array[fromIndex];
const reducedItems = reduceItems();
const newItems = addMovingItem(movingItem, reducedItems);
return newItems;
}
}
const sorter = new Sorter();
export default sorter;
import sorter from 'src/common/Sorter';
test('sortItem first item forward', () => {
const startingArray = ['a', 'b', 'c', 'd'];
const expectedArray = ['b', 'a', 'c', 'd'];
expect(sorter.sortItem(startingArray, 0, 1)).toStrictEqual(expectedArray);
});
test('sortItem middle item forward', () => {
const startingArray = ['a', 'b', 'c', 'd'];
const expectedArray = ['a', 'c', 'b', 'd'];
expect(sorter.sortItem(startingArray, 1, 2)).toStrictEqual(expectedArray);
});
test('sortItem middle item backward', () => {
const startingArray = ['a', 'b', 'c', 'd'];
const expectedArray = ['a', 'c', 'b', 'd'];
expect(sorter.sortItem(startingArray, 2, 1)).toStrictEqual(expectedArray);
});
test('sortItem last item backward', () => {
const startingArray = ['a', 'b', 'c', 'd'];
const expectedArray = ['a', 'b', 'd', 'c'];
expect(sorter.sortItem(startingArray, 3, 2)).toStrictEqual(expectedArray);
});
这个版本并不适合所有目的,也不是每个人都喜欢逗号表达式,但这里有一个纯表达式,创建了一个新的副本:
const move = (from, to, ...a) => (a.splice(to, 0, ...a.splice(from, 1)), a)
性能略有改进的版本在不需要移动的情况下返回输入数组,它仍然可以用于不可变的用途,因为数组不会改变,并且它仍然是一个纯表达式:
const move = (from, to, ...a) =>
from === to
? a
: (a.splice(to, 0, ...a.splice(from, 1)), a)
两者的调用都是
const shuffled = move(fromIndex, toIndex, ...list)
也就是说,它依靠传播来产生一个新的副本。使用固定值3的移动会危及单个表达式的属性,或非破坏性的性质,或拼接的性能优势。同样,它更像是一个满足某些标准的示例,而不是供生产使用的建议。