将数组元素从一个数组位置移到另一个数组位置
我很难搞清楚如何移动一个数组元素。 例如,给定以下内容:
var arr = [ 'a', 'b', 'c', 'd', 'e'];
我如何写一个函数在'b'
之前移动'd'
'b'
?
或'a'
'c'
之后的'c'
?
移动之后,其余元素的索引应该更新。 这意味着在第一个例子中,在移动后arr [0]会='a',arr [1] ='d'arr [2] ='b',arr [3] ='c',arr [4] = 'E'
这似乎应该是非常简单的,但我不能把头绕在它周围。
我用这个函数取得了相当不错的成绩:
Array.prototype.move = function (old_index, new_index) { if (new_index >= this.length) { var k = new_index - this.length; while ((k--) + 1) { this.push(undefined); } } this.splice(new_index, 0, this.splice(old_index, 1)[0]); return this; // for testing purposes };
示例代码: [1, 2, 3].move(0, 1)
给出[2, 1, 3]
[1, 2, 3].move(0, 1)
[2, 1, 3]
。
请注意,最后一个return
值仅用于testing目的: splice
就地对数组执行操作,因此不需要返回。 此外, move
是一个就地操作。 如果您想避免这种情况并返回副本,请使用slice
。
浏览代码:
- 如果
new_index
大于数组的长度,我们希望(我认为)用新的undefined
s正确地填充数组。 这个小代码片段通过在数组上推送undefined
来处理这个问题,直到我们拥有合适的长度。 - 然后,在
this.splice(old_index, 1)[0]
,我们this.splice(old_index, 1)[0]
了旧的元素。splice
返回被拼接的元素,但是它在一个数组中。 在我们上面的例子中,这是[1]
。 所以我们拿这个数组的第一个索引来得到raw1
。 - 然后我们使用
splice
将这个元素插入到new_index的位置。 既然我们填充了上面的数组,如果new_index > this.length
,它可能会出现在正确的地方,除非他们做了一些奇怪的事情,比如传入负数。
一个更有前途的版本来解释负面指标:
Array.prototype.move = 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) { var k = new_index - this.length; while ((k--) + 1) { this.push(undefined); } } this.splice(new_index, 0, this.splice(old_index, 1)[0]); return this; // for testing purposes };
其中应该考虑[1, 2, 3, 4, 5].move(-1, -2)
适当移动[1, 2, 3, 4, 5].move(-1, -2)
(将最后一个元素移到第二个到最后一个位置)。 结果应该是[1, 2, 3, 5, 4]
。
ChillyPenguin已经将这个版本上传到npm以方便安装: https ://www.npmjs.com/package/array.prototype.move感谢ChillyPenguin!
无论哪种方式,在你原来的问题,你会做arr.move(0, 2)
为a
后c
。 对于d
之前的b
,你会做arr.move(3, 1)
。
这里是我在JSPerf上find的一行
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; } }
我不能相信,应该都去理查德·斯卡罗特 。 它在这个性能testing中击败了较小数据集的基于拼接的方法。 然而,Darwayne指出 ,大数据集的速度要慢得多。
我喜欢这种方式。 它工作,它快速,优雅。
function arraymove(arr, fromIndex, toIndex) { var element = arr[fromIndex]; arr.splice(fromIndex, 1); arr.splice(toIndex, 0, element); }
注意:一定要记得检查你的数组边界。
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"]
因为function是可链接的,
alert(arr.move(0,2).join(','));
在这里演示
我的2c。 易于阅读,工作,速度快,不会创build新的数组。
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; }
从@Reid中得到这个想法,推动应该移动的项目的位置,以保持数组大小不变。 这确实简化了计算。 另外,推空的对象还有一个好处,就是可以在以后唯一地search它。 这是有效的,因为两个对象在引用同一个对象之前是不相等的。
({}) == ({}); // 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); }
这是基于@ Reid的解决scheme。 除:
- 我不更改
Array
原型。 - 将项目越界移到右侧不会创build
undefined
项目,只是将项目移动到最右侧的位置。
function:
function move(array, oldIndex, newIndex) { if (newIndex >= array.length) { newIndex = array.length - 1; } array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]); return array; }
unit testing:
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); }); });
Array
的splice
方法可能有所帮助: https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
只要记住它可能相对昂贵,因为它必须积极地重新索引arrays。
一种方法是使用slice方法按照所需的顺序创build一个新的数组。
例
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']
你可以实现一些基本的微积分,并创build一个将数组元素从一个位置移动到另一个位置的通用函数。
对于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
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的接受答案,只是使用Object.assign在拼接之前创build了一个数组副本。
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展示它的行动 。
Array.move.js
概要
移动数组中的元素,返回包含移动元素的数组。
句法
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; } });
我结合了其中的两个,在移动小的和大的距离的时候都会更好一些。 我得到了相当一致的结果,但是这可能会被比我更聪明的人稍微调整一下,以适应不同的尺寸等等。
移动物体时使用一些其他的方法小距离显着快于(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; } }
我使用了@Reid的一个很好的答案 ,但是从一个数组末尾向前移动一个元素到开始(像在一个循环中 )一样挣扎。 例如['a','b','c']应该通过调用.move(2,3)来变成['c','a','b'
我通过改变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 };