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

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

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

还是c右边的a ?

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

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

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


当前回答

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

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)

也就是说,它依靠传播来产生一个新的副本。使用固定值3的移动会危及单个表达式的属性,或非破坏性的性质,或拼接的性能优势。同样,它更像是一个满足某些标准的示例,而不是供生产使用的建议。

其他回答

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

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

这不是我的功劳,这都应该归功于理查德·斯卡洛特。在这个性能测试中,它在较小的数据集上击败了基于拼接的方法。然而,正如Darwayne指出的那样,在较大的数据集上,它要慢得多。

面向对象,可表达,可调试,无突变,已测试。

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

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

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

这个函数接收源数组,以及源和目标索引。你可以把它添加到数组中。原型(如果需要的话)。

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

我喜欢不可变的,函数式的语句:)…

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