将数组元素从一个数组位置移动到另一个数组位置

我很难弄清楚如何移动数组的元素。例如,给定以下内容:

var array = [ 'a', 'b', 'c', 'd', 'e'];

如何编写一个函数将元素'd'移动到'b'的左侧?

或者'c'右边的'a'

移动元素后,应更新其余元素的索引。生成的数组将是:

array = ['a', 'd', 'b', 'c', 'e']

这似乎应该很简单,但我不能把我的头绕在它周围。

702213 次浏览

一种方法是使用切片方法创建一个新的数组,其中包含您想要的顺序。

示例

var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
  • arr.slice(0,1)给你['a']
  • arr.slice(2,4)给出['b','c']
  • arr.slice(4)给你['e']

Arraysplice方法可能会有所帮助:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

请记住,它可能相对昂贵,因为它必须主动重新索引数组。

如果您想要npm上的版本,数组移动最接近此答案,尽管它不是相同的实现。有关更多详细信息,请参阅其用法部分。此答案的先前版本(修改Array.prototype.move)可以在npm上的array.prototype.move上找到。


我在这个功能上取得了相当大的成功:

function array_move(arr, old_index, new_index) {
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
};


// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1)); 

请注意,最后一个return只是为了测试目的:splice就地对数组执行操作,因此不需要返回。通过扩展,此move是一个就地操作。如果您想避免这种情况并返回副本,请使用slice

逐步通过代码:

  1. 如果new_index大于数组的长度,我们希望(我假设)用新的undefined正确填充数组。这个小片段通过在数组上推送undefined来处理这个问题,直到我们有适当的长度。
  2. 然后,在arr.splice(old_index, 1)[0]中,我们拼接旧元素。splice返回拼接出来的元素,但它在一个数组中。在我们上面的例子中,这是[1]。所以我们取该数组的第一个索引来获取原始的1
  3. 然后我们使用splice将这个元素插入new_index的位置。由于我们在上面填充了数组ifnew_index > arr.length,它可能会出现在正确的位置,除非他们做了一些奇怪的事情,比如传入负数。

一个更好的版本来解释负指数:

function array_move(arr, old_index, new_index) {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing purposes
};
    

// returns [1, 3, 2]
console.log(array_move([1, 2, 3], -1, -2));

这应该正确地解释array_move([1, 2, 3], -1, -2)之类的事情(将最后一个元素移动到倒数第二个位置)。结果应该是[1, 3, 2]

无论哪种方式,在你最初的问题中,你会在c之后为aarray_move(arr, 0, 2)。对于b之前的d,你会做array_move(arr, 3, 1)

从@Reid那里得到了这个想法,即在应该移动以保持数组大小不变的项目的位置推送一些东西。这确实简化了计算。此外,推送空对象还有一个额外的好处,即能够在以后唯一搜索它。这很有效,因为两个对象在引用同一个对象之前是不相等的。

({}) == ({}); // false

这是接收源数组和源、目标索引的函数。如果需要,您可以将其添加到Array.prototype。

function moveObjectAtIndex(array, sourceIndex, destIndex) {
var placeholder = {};
// remove the object from its initial position and
// plant the placeholder object in its place to
// keep the array length constant
var objectToMove = array.splice(sourceIndex, 1, placeholder)[0];
// place the object in the desired position
array.splice(destIndex, 0, objectToMove);
// take out the temporary object
array.splice(array.indexOf(placeholder), 1);
}

我喜欢这种方式。它简洁而有效。

function arraymove(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}

注意:永远记得检查你的数组边界。

在jsFiddle中运行Snippet

这是我在JSPerf上找到的一个衬垫……

Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};

这读起来很棒,但是如果你想要性能(在小数据集中),请尝试…

 Array.prototype.move2 = function(pos1, pos2) {
// local variables
var i, tmp;
// cast input parameters to integers
pos1 = parseInt(pos1, 10);
pos2 = parseInt(pos2, 10);
// if positions are different and inside array
if (pos1 !== pos2 && 0 <= pos1 && pos1 <= this.length && 0 <= pos2 && pos2 <= this.length) {
// save element from position 1
tmp = this[pos1];
// move element down and shift other elements up
if (pos1 < pos2) {
for (i = pos1; i < pos2; i++) {
this[i] = this[i + 1];
}
}
// move element up and shift other elements down
else {
for (i = pos1; i > pos2; i--) {
this[i] = this[i - 1];
}
}
// put element from position 1 to destination
this[pos2] = tmp;
}
}

我不能接受任何荣誉,它应该全部转到理查德·斯卡罗特。它在这个性能测试中击败了基于拼接的方法用于较小的数据集。然而,在较大的数据集上正如达尔文所指出的显着较慢。

您可以实现一些基本的微积分并创建一个通用函数,用于将数组元素从一个位置移动到另一个位置。

对于JavaScript,它看起来像这样:

function magicFunction (targetArray, indexFrom, indexTo) {


targetElement = targetArray[indexFrom];
magicIncrement = (indexTo - indexFrom) / Math.abs (indexTo - indexFrom);


for (Element = indexFrom; Element != indexTo; Element += magicIncrement){
targetArray[Element] = targetArray[Element + magicIncrement];
}


targetArray[indexTo] = targetElement;


}

查看“Gloom物质”中的“移动数组元素”以获得详细解释。

https://web.archive.org/web/20121105042534/http://www.gloommatter.com:80/DDesign/programming/moving-any-array-elements-universal-function.html

Array.move.js

总结

在数组中移动元素,返回包含移动元素的数组。

语法

array.move(index, howMany, toIndex);

参数范围

索引:移动元素的索引。如果为负,索引将从末尾开始。

多少:从索引移动的元素数。

toIndex:放置移动元素的数组的索引。如果为负,toIndex将从末尾开始。

用法

array = ["a", "b", "c", "d", "e", "f", "g"];


array.move(3, 2, 1); // returns ["d","e"]


array; // returns ["a", "d", "e", "b", "c", "f", "g"]

Poly填充

Array.prototype.move || Object.defineProperty(Array.prototype, "move", {
value: function (index, howMany, toIndex) {
var
array = this,
index = parseInt(index) || 0,
index = index < 0 ? array.length + index : index,
toIndex = parseInt(toIndex) || 0,
toIndex = toIndex < 0 ? array.length + toIndex : toIndex,
toIndex = toIndex <= index ? toIndex : toIndex <= index + howMany ? index : toIndex - howMany,
moved;


array.splice.apply(array, [toIndex, 0].concat(moved = array.splice(index, howMany)));


return moved;
}
});

最后,我结合了其中的两个,以便在移动小距离和大距离时更好地工作。我得到了相当一致的结果,但这可能会被比我聪明的人稍微调整一下,以不同的方式处理不同的尺寸,等等。

当移动物体小距离时使用其他一些方法比使用拼接要快得多(x10)。这可能会根据数组长度而变化,但对于大型数组来说确实如此。

function ArrayMove(array, from, to) {
if ( Math.abs(from - to) > 60) {
array.splice(to, 0, array.splice(from, 1)[0]);
} else {
// works better when we are not moving things very far
var target = array[from];
var inc = (to - from) / Math.abs(to - from);
var current = from;
for (; current != to; current += inc) {
array[current] = array[current + inc];
}
array[to] = target;
}
}

https://web.archive.org/web/20181026015711/https://jsperf.com/arraymove-many-sizes

plice()方法在数组中添加/删除项目,并返回删除项目。

注意:此方法更改原始数组。 /w3schools/

Array.prototype.move = function(from,to){
this.splice(to,0,this.splice(from,1)[0]);
return this;
};


var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(3,1);//["a", "d", "b", "c", "e"]




var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(0,2);//["b", "c", "a", "d", "e"]

由于函数是链式,因此也可以:

alert(arr.move(0,2).join(','));

演示

我的2c。易于阅读,它有效,它很快,它不会创建新的数组。

function move(array, from, to) {
if( to === from ) return array;


var target = array[from];
var increment = to < from ? -1 : 1;


for(var k = from; k != to; k += increment){
array[k] = array[k + increment];
}
array[to] = target;
return array;
}
    Array.prototype.moveUp = function (value, by) {
var index = this.indexOf(value),
newPos = index - (by || 1);


if (index === -1)
throw new Error("Element not found in array");


if (newPos < 0)
newPos = 0;


this.splice(index, 1);
this.splice(newPos, 0, value);
};


Array.prototype.moveDown = function (value, by) {
var index = this.indexOf(value),
newPos = index + (by || 1);


if (index === -1)
throw new Error("Element not found in array");


if (newPos >= this.length)
newPos = this.length;


this.splice(index, 1);
this.splice(newPos, 0, value);
};






var arr = ['banana', 'curyWurst', 'pc', 'remembaHaruMembaru'];


alert('withiout changes= '+arr[0]+' ||| '+arr[1]+' ||| '+arr[2]+' ||| '+arr[3]);
arr.moveDown(arr[2]);




alert('third word moved down= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
arr.moveUp(arr[2]);
alert('third word moved up= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);

我使用了漂亮的@Reid的回答,但是很难将元素从数组的末尾移动到开头(就像循环一样)。 例如:['a','b','c']应该变成['c','a','b']通过调用. mobile(2,3)

我通过改变new_index>=this.length.

Array.prototype.move = function (old_index, new_index) {
console.log(old_index + " " + new_index);
while (old_index < 0) {
old_index += this.length;
}
while (new_index < 0) {
new_index += this.length;
}
if (new_index >= this.length) {
new_index = new_index % this.length;
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};

这是基于@Reid的解决方案。除了:

  • 我不会改变Array原型。
  • 将项向右移动出界不会创建undefined项,它只是将项移动到最右侧的位置。

功能:

function move(array, oldIndex, newIndex) {
if (newIndex >= array.length) {
newIndex = array.length - 1;
}
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
return array;
}

单元测试:

describe('ArrayHelper', function () {
it('Move right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 0, 1);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
})
it('Move left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 0);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, -2);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 4);
assert.equal(array[0], 1);
assert.equal(array[1], 3);
assert.equal(array[2], 2);
});
});

我需要一个不可变的移动方法(一个不改变原始数组的方法),所以我调整了@Reid接受的答案,在进行拼接之前简单地使用Object.assign创建数组的副本。

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显示它在行动

我在这里实现了一个基于@Merc答案的不可变ECMAScript 6解决方案:

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;
};

变量名可以缩短,只使用长的,以便代码可以解释自己。

在很多地方(将自定义函数添加到Array.prototype)都说使用Array原型可能是个坏主意,不管怎样,我结合了各种帖子中最好的,我用现代Javascript带来了这个:

    Object.defineProperty(Array.prototype, 'immutableMove', {
enumerable: false,
value: 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
}
});


//how to use it
myArray=[0, 1, 2, 3, 4];
myArray=myArray.immutableMove(2, 4);
console.log(myArray);
//result: 0, 1, 3, 4, 2

希望对任何人都有用

这是我的单衬ES6解决方案,带有可选参数on

if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
this.splice(to, 0, ...this.splice(from, on))
}
}

适应digiguru提出的第一个解决方案

参数on是从from开始要移动的元素数。

这是一个可链接的变体:

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的回答很好的补充(因为我无法评论); 您可以使用模来使负索引和过大的索引“滚动”:

function array_move(arr, old_index, new_index) {
new_index =((new_index % arr.length) + arr.length) % arr.length;
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
}


// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1)); 

这个版本并不适合所有目的,也不是每个人都喜欢逗号表达式,但这里有一个单行代码,它是一个纯表达式,创建了一个新的副本:

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)

即它依赖于传播来生成一个新的副本。使用固定的3move会危及splice的单表达式属性、非破坏性性质或性能优势。同样,它更像是一个符合某些标准的示例,而不是生产使用的建议。

let ar = ['a', 'b', 'c', 'd'];


function change( old_array, old_index , new_index ){


return old_array.map(( item , index, array )=>{
if( index === old_index ) return array[ new_index ];
else if( index === new_index ) return array[ old_index ];
else return item;
});


}


let result = change( ar, 0, 1 );


console.log( result );

结果:

["b", "a", "c", "d"]

const move = (from, to, ...a) =>from === to ? a : (a.splice(to, 0, ...a.splice(from, 1)), a);
const moved = move(0, 2, ...['a', 'b', 'c']);
console.log(moved)

    let oldi, newi, arr;
    

if(newi !== oldi) {
let el = this.arr.splice(oldi, 1);
if(newi > oldi && newi === (this.arr.length + 2)) {
this.arr.push("");
}
this.arr.splice(newi, 0, el);
if(newi > oldi && newi === (this.arr.length + 2)) {
this.arr.pop();
}
}

var ELEMS = ['a', 'b', 'c', 'd', 'e'];
/*
Source item will remove and it will be placed just after destination
*/
function moveItemTo(sourceItem, destItem, elements) {
var sourceIndex = elements.indexOf(sourceItem);
var destIndex = elements.indexOf(destItem);
if (sourceIndex >= -1 && destIndex > -1) {
elements.splice(destIndex, 0, elements.splice(sourceIndex, 1)[0]);
}
return elements;
}
console.log('Init: ', ELEMS);
var result = moveItemTo('a', 'c', ELEMS);
console.log('BeforeAfter: ', result);

我以为这是一个交换问题,但事实并非如此。这是我的一行解决方案:

const move = (arr, from, to) => arr.map((item, i) => i === to ? arr[from] : (i >= Math.min(from, to) && i <= Math.max(from, to) ? arr[i + Math.sign(to - from)] : item));

这里有一个小测试:

let test = ['a', 'b', 'c', 'd', 'e'];
console.log(move(test, 0, 2)); // [ 'b', 'c', 'a', 'd', 'e' ]
console.log(move(test, 1, 3)); // [ 'a', 'c', 'd', 'b', 'e' ]
console.log(move(test, 2, 4)); // [ 'a', 'b', 'd', 'e', 'c' ]
console.log(move(test, 2, 0)); // [ 'c', 'a', 'b', 'd', 'e' ]
console.log(move(test, 3, 1)); // [ 'a', 'd', 'b', 'c', 'e' ]
console.log(move(test, 4, 2)); // [ 'a', 'b', 'e', 'c', 'd' ]
console.log(move(test, 4, 0)); // [ 'e', 'a', 'b', 'c', 'd' ]

没有数组复制的不可变版本:

const moveInArray = (arr, fromIndex, toIndex) => {
if (toIndex === fromIndex || toIndex >= arr.length) return arr;


const toMove = arr[fromIndex];
const movedForward = fromIndex < toIndex;


return arr.reduce((res, next, index) => {
if (index === fromIndex) return res;
if (index === toIndex) return res.concat(
movedForward ? [next, toMove] : [toMove, next]
);


return res.concat(next);
}, []);
};

我认为最好的方法是为数组定义一个新属性

Object.defineProperty(Array.prototype, 'move', {
value: function (old_index, new_index) {
while (old_index < 0) {
old_index += this.length;
}
while (new_index < 0) {
new_index += this.length;
}
if (new_index >= this.length) {
let k = new_index - this.length;
while ((k--) + 1) {
this.push(undefined);
}
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this;
}
});


console.log([10, 20, 30, 40, 50].move(0, 1));  // [20, 10, 30, 40, 50]
console.log([10, 20, 30, 40, 50].move(0, 2));  // [20, 30, 10, 40, 50]

另一个使用ES6数组扩展算子的纯JS变体,没有突变

const reorder = (array, sourceIndex, destinationIndex) => {
const smallerIndex = Math.min(sourceIndex, destinationIndex);
const largerIndex = Math.max(sourceIndex, destinationIndex);


return [
...array.slice(0, smallerIndex),
...(sourceIndex < destinationIndex
? array.slice(smallerIndex + 1, largerIndex + 1)
: []),
array[sourceIndex],
...(sourceIndex > destinationIndex
? array.slice(smallerIndex, largerIndex)
: []),
...array.slice(largerIndex + 1),
];
}


// returns ['a', 'c', 'd', 'e', 'b', 'f']
console.log(reorder(['a', 'b', 'c', 'd', 'e', 'f'], 1, 4))
      

此方法将保留原始数组,并检查边界错误。

const move = (from, to, arr) => {
to = Math.max(to,0)
from > to
? [].concat(
arr.slice(0,to),
arr[from],
arr.filter((x,i) => i != from).slice(to))
: to > from
? [].concat(
arr.slice(0, from),
arr.slice(from + 1, to + 1),
arr[from],
arr.slice(to + 1))
: arr}

这是一个非常简单的方法使用拼接

Array.prototype.moveToStart = function(index) {
this.splice(0, 0, this.splice(index, 1)[0]);
return this;
};

在您的示例中,因为是string的数组,我们可以使用排名对象对字符串数组进行重新排序:

let rank =  { 'a': 0, 'b': 1, 'c': 2, 'd': 0.5, 'e': 4 };
arr.sort( (i, j) => rank[i] - rank[j] );

我们可以使用这种方法编写一个在字符串数组上工作的move函数:

function stringArrayMove(arr, from, to)
{
let rank = arr.reduce( (p, c, i) => ( p[c] = i, p ), ({ }) );
// rank = { 'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4 }
rank[arr[from]] = to - 0.5;
// rank = { 'a': 0, 'b': 1, 'c': 2, 'd': 1.5, 'e': 4 }
arr.sort( (i, j) => rank[i] - rank[j] );
// arr = [ 'a', 'd', 'b', 'c', 'e' ];
}


let arr = [ 'a', 'b', 'c', 'd', 'e' ];
stringArrayMove(arr, 3, 1);
console.log( JSON.stringify(arr) );

但是,如果我们想要排序的是一个对象数组,我们可以将排名作为每个对象的新属性引入,即:

let arr = [ { value: 'a', rank: 0 },
{ value: 'b', rank: 1 },
{ value: 'c', rank: 2 },
{ value: 'd', rank: 0.5 },
{ value: 'e', rank: 4 } ];
arr.sort( (i, j) => i['rank'] - j['rank'] );

我们可以使用Symbol来隐藏此属性的可见性,即它不会显示在JSON.stringify中。我们可以在objectArrayMove函数中泛化它:

function objectArrayMove(arr, from, to) {
let rank = Symbol("rank");
arr.forEach( (item, i) => item[rank] = i );
arr[from][rank] = to - 0.5;
arr.sort( (i, j) => i[rank] - j[rank]);
}
let arr = [ { value: 'a' }, { value: 'b' }, { value: 'c' }, { value: 'd' }, { value: 'e' } ];
console.log( 'array before move: ', JSON.stringify( arr ) );
// array before move:  [{"value":"a"},{"value":"b"},{"value":"c"},{"value":"d"},{"value":"e"}]
objectArrayMove(arr, 3, 1);
console.log( 'array after move: ', JSON.stringify( arr ) );
// array after move:  [{"value":"a"},{"value":"d"},{"value":"b"},{"value":"c"},{"value":"e"}]

如果对象嵌套:

  let array = ['a', 'b', 'c', 'd', 'e'];
let existingElement = JSON.parse(JSON.stringify(array[3]));
array.splice(1, 0, existingElement);
array.splice(4, 1);
console.log(array)

这是一种以不可变方式做到这一点的方法。它处理负数以及额外的奖励。与编辑原始数组相比,这以牺牲性能为代价减少了可能的错误数量。

const numbers = [1, 2, 3];
const moveElement = (array, from, to) => {
const copy = [...array];
const valueToMove = copy.splice(from, 1)[0];
copy.splice(to, 0, valueToMove);
return copy;
};


console.log(moveElement(numbers, 0, 2))
// > [2, 3, 1]
console.log(moveElement(numbers, -1, -3))
// > [3, 1, 2]

我喜欢不可变的、函数式的单行:)…

const swapIndex = (array, from, to) => (
from < to
? [...array.slice(0, from), ...array.slice(from + 1, to + 1), array[from], ...array.slice(to + 1)]
: [...array.slice(0, to), array[from], ...array.slice(to, from), ...array.slice(from + 1)]
);

一种方法是使用splice()从数组中删除项,然后再次使用splice()方法,将删除的项插入目标指数

const array = ['a', 'b', 'c', 'd', 'e']


const newArray = moveItem(array, 3, 1) // move element from index 3 to index 1


function moveItem(arr, fromIndex, toIndex){
let itemRemoved = arr.splice(fromIndex, 1) // assign the removed item as an array
arr.splice(toIndex, 0, itemRemoved[0]) // insert itemRemoved into the target index
return arr
}


console.log(newArray)

我使用immutability-helper库解决了我的问题。

import update from 'immutability-helper';


const move = (arr: any[], from: number, to: number) => update(arr, {
$splice: [
[from, 1],
[to, 0, arr[from] as string],
],
});


const testArray = ['a', 'b', 'c', 'd', 'e'];
console.log(move(testArray, 1, 3)); // [ 'c', 'b', 'c', 'd', 'e' ]
console.log(move(testArray, 4, 0)); // [ 'e', 'b', 'c', 'd', 'a' ]

面向对象,具有表现力,可调试,无需修改,经过测试。

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);
});

查找并将元素从“n”个位置移动到第0个位置。

例如:找到并移动'd'到第0个位置:

let arr = [ 'a', 'b', 'c', 'd', 'e'];
arr = [...arr.filter(item => item === 'd'), ...arr.filter(item => item !== 'd')];

与一切一样,充分利用才是最重要的。

对于单个移动,以及小型和大型数据集,这里都有非常好的答案。 如果你做了成千上万的动作,我建议看看州和不太频繁的密集操作。比如:

  • 更改您的数据集,针对每个项目保留订单“状态”。
  • 应用数以千计的更新。
  • 对该order属性执行单一排序。
 ["a", "b", "c"]

将改为

[
{val: 'a', order: 0},
{val: 'b', order: 1},
{val: 'c', order: 2},


]

然后,应用数千个更新。

最后,按“order”变量排序。 #36825;的东西,也?

我还没有测试过它的性能,但可以想象,在一定的使用水平上,它比每1000次尝试重建数组要好。

TypeScript版本

复制自@Merc的回答。我最喜欢那个,因为它没有创建新的数组并修改数组。我所做的只是更新到ES6并添加类型。

export function moveItemInArray<T>(workArray: T[], fromIndex: number, toIndex: number): T[] {
if (toIndex === fromIndex) {
return workArray;
}
const target = workArray[fromIndex];
const increment = toIndex < fromIndex ? -1 : 1;


for (let k = fromIndex; k !== toIndex; k += increment) {
workArray[k] = workArray[k + increment];
}
workArray[toIndex] = target;
return workArray;
}

我们可以通过多种方式将数组元素从一个位置移动到另一个位置。在这里,我尝试以3种不可变的方式解决这个问题。

使用splice移动数组元素,其中时间复杂度为二次时间-O(n^2)

function arrayMove(arr, oldIndex, newIndex) {
const copiedArr = [...arr];
const length = copiedArr.length;
  

if (oldIndex !== newIndex && length > oldIndex && length > newIndex) {
copiedArr.splice(newIndex, 0, copiedArr.splice(oldIndex, 1)[0]);
}
  

return copiedArr;
}


arrayMove([1,2,3,4], 0, 3) // [2,3,4,1]

使用flatMap移动数组元素,其中时间复杂度为线性时间-O(n)

function arrayMove(arr, oldIndex, newIndex) {
const length = arr.length;
const itemToMove = arr[oldIndex]


if (oldIndex === newIndex || oldIndex > length || newIndex > length) {
return arr;
}


return arr.flatMap((item, index) => {
if (index === oldIndex) return [];
if (index === newIndex) return oldIndex < newIndex ? [item, itemToMove] : [itemToMove, item];
return item;
})
}


arrayMove([1,2,3,4], 0, 3) // [2,3,4,1]

使用reduce移动数组元素,其中时间复杂度为线性时间-O(n)

function arrayMove(arr, oldIndex, newIndex) {
const length = arr.length;
const itemToMove = arr[oldIndex]


if (oldIndex === newIndex || oldIndex > length || newIndex > length) {
return arr;
}


return arr.reduce((acc, item, index) => {
if (index === oldIndex) return acc;
if (index === newIndex) return oldIndex < newIndex ? [...acc, item, itemToMove] : [...acc, itemToMove, item];
return [...acc, item];
}, [])
}


arrayMove([1,2,3,4], 0, 3) // [2,3,4,1]

您也可以查看此要点:将数组元素从一个数组位置移动到另一个数组位置

2022年,这个打字稿实用程序将与单元测试一起工作。

export const arrayMove = (arr: any[], fromIndex: number, toIndex: number) => {
const newArr = [...arr];
newArr.splice(toIndex, 0, newArr.splice(fromIndex, 1)[0]);
return newArr;
};

const testArray = ['1', '2', '3', '4'];


describe('arrayMove', () => {
it('should move array item to toIndex', () => {
expect(arrayMove(testArray, 2, 0)).toEqual(['3', '1', '2', '4']);
expect(arrayMove(testArray, 3, 1)).toEqual(['1', '4', '2', '3']);
expect(arrayMove(testArray, 1, 2)).toEqual(['1', '3', '2', '4']);
expect(arrayMove(testArray, 0, 2)).toEqual(['2', '3', '1', '4']);
});
});