Переместить элемент массива из одной позиции массива в другую

Мне сложно определить, как перемещать элемент массива. Например, учитывая следующее:

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] = 'е'

Кажется, это должно быть довольно просто, но я не могу обернуть вокруг себя.

Ответ 1

Если вам нужна версия для 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)); 

Ответ 2

Вот один лайнер, который я нашел на 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. Он превосходит метод сплайсинга для меньших наборов данных в этом тесте . Тем не менее он значительно медленнее в больших наборах данных , как указывает Дарвейн.

Ответ 3

Мне нравится этот способ. Это работает, это сжато и изящно.

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

Примечание: всегда не забывайте проверять границы вашего массива.

Вот jsFiddle для тестирования: https://jsfiddle.net/aq9Laaew/286055/

Ответ 4

Метод splice() добавляет/удаляет элементы в/из массива и возвращает удаленные элементы.

Примечание. Этот метод изменяет исходный массив. /W 3schools/

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

демо здесь

Ответ 5

Мой 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;
}

Ответ 6

Получил эту идею от @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);
}

Ответ 7

Это основано на решении @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);
    });
});

Ответ 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 вы хотите переместиться.

Ответ 9

Один из подходов заключался бы в создании нового массива с фрагментами в нужном порядке с использованием метода среза.

Пример

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

Ответ 11

Вы можете реализовать некоторое базовое исчисление и создать универсальную функцию для перемещения элемента массива из одной позиции в другую.

Для 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

Ответ 12

Мне был нужен неизменный метод перемещения (тот, который не изменял исходный массив), поэтому я адаптировал принятый ответ @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, показывающий это в действии.

Ответ 13

    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

Ответ 14

Я реализовал неизменное решение 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

Array.move.js

Резюме

Перемещает элементы внутри массива, возвращая массив, содержащий перемещенные элементы.

Синтаксис

array.move(index, howMany, toIndex);

Параметры

index: индекс, по которому нужно перемещать элементы. Если отрицательный, индекс начнется с конца.

howMany: количество элементов для перемещения из 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"]

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

Ответ 16

В итоге я объединил два из них, чтобы немного поработать, двигаясь как на большие, так и на большие расстояния. Я получаю довольно последовательные результаты, но это, вероятно, может быть немного изменено кем-то умнее меня, чтобы работать по-разному для разных размеров и т.д.

Использование некоторых других методов при перемещении объектов на малых расстояниях было значительно быстрее (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

Ответ 17

Эта версия не идеальна для всех целей, и не всем нравятся выражения с запятыми, но здесь это строка с одним выражением, которая является чистым выражением и создает свежую копию:

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)

то есть он основан на распространении для создания новой копии. Использование move с фиксированной арностью 3 поставит под угрозу либо свойство отдельного выражения, либо неразрушающий характер, либо выигрыш в производительности splice. Опять же, это скорее пример, который соответствует некоторым критериям, чем предложение для производственного использования.

Ответ 18

Я использовал хороший ответ @Reid, но боролся с перемещением элемента из конца массива на один шаг дальше - в начало (например, в цикл). Например. ['a', 'b', 'c'] должны стать ['c', 'a', 'b'], вызывая .move(2,3)

Я достиг этого, изменив случай для 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
    };

Ответ 19

Во многих местах сказано (добавление пользовательских функций в 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

Надежда может быть полезна каждому

Ответ 20

В дополнение к Рейду отличный ответ (и потому что я не могу комментировать); Вы можете использовать модуль по модулю, чтобы заставить "отрицательные индексы" и слишком большие индексы "пролонгировать":

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

Ответ 21

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

Ответ 22

У меня было это в узле и PWA (ESM, Вавилон):

// extract old item
let old = items.splice(from, 1).pop(); // splice returns an array, we need the 1 item only
// insert at new pos
items.splice(to -1, 0, old); // array is now shorter, me need to subtract 1 from the position

Ответ 23

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)