我很难弄清楚如何移动数组元素。例如,给出以下内容:
var arr = [ 'a', 'b', 'c', 'd', 'e'];
如何在'd'
之前编写一个移动'b'
的函数?
'a'
之后的'c'
?
移动后,应更新其余元素的索引。这意味着在第一个例子中,移动arr [0]将='a',arr [1] ='d'arr [2] ='b',arr [3] ='c',arr [4] = 'e' 的
这看起来应该很简单,但我无法绕过它。
答案 0 :(得分:582)
如果你想在npm上使用某个版本,array-move是最接近这个答案的,尽管它的实现并不相同。有关详细信息,请参阅其用法部分。可以在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
。
单步执行代码:
new_index
大于数组的长度,我们希望(我推测)用新的undefined
正确填充数组。这个小片段通过在阵列上按undefined
来处理这个问题,直到我们有适当的长度。arr.splice(old_index, 1)[0]
中,我们拼出旧元素。 splice
返回拼接出来的元素,但它在一个数组中。在上面的示例中,这是[1]
。因此,我们采用该数组的第一个索引来获取原始1
。splice
将此元素插入new_index的位置。由于我们在new_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]
。
无论哪种方式,在原始问题中,您array_move(arr, 0, 2)
之后a
c
d
。对于b
之前的array_move(arr, 3, 1)
,您可以执行{{1}}。
答案 1 :(得分:232)
这是我在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;
}
}
我不能相信,它应该全部转到Richard Scarrott。它击败了基于拼接的方法,用于此performance test中的较小数据集。但是,对于较大的数据集as Darwayne points out,它会明显变慢。
答案 2 :(得分:148)
我喜欢这样。它有效,简洁而优雅。
function arraymove(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
注意:请记住检查数组范围。
这是一个要测试的jsFiddle:https://jsfiddle.net/aq9Laaew/286055/
答案 3 :(得分:30)
splice()方法在数组中添加/删除项目,并返回删除的项目。
注意:此方法更改原始数组。 / 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"]
因为函数是chainable,这也有效:
alert(arr.move(0,2).join(','));
答案 4 :(得分:24)
我的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;
}
答案 5 :(得分:16)
从@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);
}
答案 6 :(得分:15)
这是基于@ 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);
});
});
答案 7 :(得分:8)
以下是我的单线程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
开始的元素数。
答案 8 :(得分:7)
一种方法是使用切片方法创建一个包含所需顺序的新数组。
实施例
var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
答案 9 :(得分:6)
splice
的{{1}}方法可能有所帮助:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
请记住,它可能相对昂贵,因为它必须主动重新索引数组。
答案 10 :(得分:6)
你可以实现一些基本的微积分并创建一个通用函数,用于将数组元素从一个位置移动到另一个位置。
对于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;
}
查看“gloommatter”中的“移动数组元素”以获取详细说明。
http://www.gloommatter.com/DDesign/programming/moving-any-array-elements-universal-function.html
答案 11 :(得分:5)
我需要一个不可变的移动方法(一个不改变原始数组的方法),所以我调整了@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;
};
答案 12 :(得分:4)
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]);
答案 13 :(得分:4)
使用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))
答案 14 :(得分:3)
我已根据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;
};
变量名称可以缩短,只使用长变量名称,以便代码可以解释自己。
答案 15 :(得分:2)
我最终结合其中两个在移动小距离和大距离时更好地工作。我得到了相当一致的结果,但这可能会被比我更聪明的人稍微调整一下,以不同的尺寸等工作。
使用其他一些方法移动物体时,距离比使用接头快得多(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;
}
}
答案 16 :(得分:2)
移动数组中的元素,返回包含移动元素的数组。
array.move(index, howMany, toIndex);
index :移动元素的索引。如果为负数,索引将从结尾开始。
howMany :要从索引移动的元素数量。
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"]
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;
}
});
答案 17 :(得分:1)
这是使用拼接的非常简单的方法
Array.prototype.moveToStart = function(index) {
this.splice(0, 0, this.splice(index, 1)[0]);
return this;
};
答案 18 :(得分:1)
据说很多地方(adding custom functions into Array.prototype)玩数组原型可能是一个坏主意,无论如何我总结了各种帖子中最好的,我带着这个,使用现代的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
希望对任何人都有用
答案 19 :(得分:1)
我以为这是交换问题,但不是。这是我的一线解决方案:
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' ]
答案 20 :(得分: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)
即它依靠传播来生成新副本。使用固定的arity 3 move
可能会损害单一表达式属性,非破坏性或splice
的性能优势。同样,它更像是符合某些标准的示例,而不是针对生产用途的建议。
答案 21 :(得分:1)
作为Reid's excellent answer的补充(因为我无法发表评论); 您可以使用modulo使负指数和太大的指数“翻转”:
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));
答案 22 :(得分:1)
我使用了很好的answer of @Reid,但是在将一个元素从数组的末尾向前移动一步 - 到开头(就像在循环中)一样艰难。 例如。 [&#39; a&#39;,&#39; b&#39;,&#39; c&#39;]应该成为[&#39; c&#39;,&#39; a&#39;,& #39; b&#39;]通过调用.move(2,3)
我通过更改new_index&gt; = 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
};
答案 23 :(得分:0)
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"]
答案 24 :(得分:0)
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)
答案 25 :(得分:0)
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();
}
}
答案 26 :(得分:0)
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);
答案 27 :(得分:0)
没有数组副本的不变版本:
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);
}, []);
};
答案 28 :(得分:0)
我认为最好的方法是为数组定义一个新属性
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]
答案 29 :(得分:0)
此方法将保留原始数组,并检查边界错误。
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}
答案 30 :(得分:0)
在您的示例中,由于是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"}]
答案 31 :(得分:0)
如果对象是嵌套的:
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)
答案 32 :(得分:0)
这是一种方法。它处理负数以及增加的奖励。
step1