我很难弄清楚如何移动数组中的一个元素。例如,给定以下条件:

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

我怎么能写一个函数来移动元素'd'到'b'的左边?

还是c右边的a ?

移动元素之后,应该更新其余元素的索引。结果数组将是:

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

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


当前回答

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

我不会改变数组原型。 向右移动一个项目并不会创建未定义的项目,它只是将项目移动到最右边的位置。

功能:

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

其他回答

我已经实现了一个不可变的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;
};

变量名可以缩短,只使用长变量名,这样代码就可以解释自己。

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

Polyfill

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

另一个纯JS变体使用ES6数组展开运算符,没有突变

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

如果你想要npm上的一个版本,array-move是最接近这个答案的,尽管它不是相同的实现。更多细节请参见其用法部分。这个答案的前一个版本(修改了Array.prototype.move)可以在npm的Array.prototype.move中找到。


我在这个函数上做得相当成功:

函数array_move(arr, old_index, new_index) { If (new_index >= arr.length) { Var k = new_index - arr。长度+ 1; 当(k——){ arr.push(定义); } } 加勒比海盗。拼接(new_index, 0, arr.)拼接(old_index, 1) [0]); 返回arr;//用于测试 }; //返回[2,1,3] Console.log (array_move([1,2,3], 0,1));

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

逐步执行代码:

If new_index is greater than the length of the array, we want (I presume) to pad the array properly with new undefineds. This little snippet handles this by pushing undefined on the array until we have the proper length. Then, in arr.splice(old_index, 1)[0], we splice out the old element. splice returns the element that was spliced out, but it's in an array. In our above example, this was [1]. So we take the first index of that array to get the raw 1 there. Then we use splice to insert this element in the new_index's place. Since we padded the array above if new_index > arr.length, it will probably appear in the right place, unless they've done something strange like pass in a negative number.

解释负指数的一个更花哨的版本:

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之后的a,你需要做array_move(arr, 0,2)对于b之前的d,你需要做array_move(arr, 3,1)。

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

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

const testArray = ['1', '2', '3', '4']; description ('arrayMove', () => { it('应该将数组项移动到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']); }); });