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

问题描述 投票:410回答:21

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

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'

这看起来应该很简单,但我无法绕过它。

javascript arrays
21个回答
605
投票

如果您想在npm上使用某个版本,array-move最接近这个答案,尽管它的实现并不相同。有关详细信息,请参阅其用法部分。这个答案的先前版本(修改过的Array.prototype.move)可以在array.prototype.move的npm找到。


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

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大于数组的长度,我们希望(我推测)用新的undefineds正确填充数组。这个小片段通过在阵列上推动undefined来处理这个问题,直到我们有适当的长度。
  2. 然后,在arr.splice(old_index, 1)[0],我们拼出旧元素。 splice返回拼接出来的元素,但它在一个数组中。在上面的例子中,这是[1]。所以我们采用该数组的第一个索引来获取那里的原始1
  3. 然后我们使用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)之后为ac。对于d之前的b,你会做array_move(arr, 3, 1)


6
投票

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

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


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


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

这是一个jsfiddle showing it in action


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

http://plnkr.co/edit/JaiAaO7FQcdPGPY6G337?p=preview


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

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


2
投票

Array.move.js

摘要

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

句法

array.move(index, howMany, toIndex);

参数

index:移动元素的索引。如果为负数,则索引将从结尾开始。

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

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

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

http://jsperf.com/arraymove-many-sizes


2
投票

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

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的单一表达属性,或非破坏性或性能优势。同样,它更像是一个符合某些标准而不是生产用途建议的例子。


1
投票

我使用了漂亮的answer of @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
    };

1
投票

据说很多地方(adding custom functions into 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

希望对任何人都有用


239
投票

这是我在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,它会明显变慢。


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

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"]

164
投票

我喜欢这样。它有效,简洁而优雅。

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

注意:请记住检查数组边界。

这是一个jsFiddle测试:https://jsfiddle.net/aq9Laaew/286055/


32
投票

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(','));

demo here


25
投票

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

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

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

9
投票

这是我的单线程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开始的元素数。


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) );
  • arr.slice(0,1)给你['a']
  • arr.slice(2,4)给你['b','c']
  • arr.slice(4)给你['e']
© www.soinside.com 2019 - 2024. All rights reserved.