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

522

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

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] = «е»

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

Марк Браун
источник
3
ну этот вопрос старый, но золотой
Джалал
используя ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
Мухсалаа
Это просто меняет элементы в initи target.
Мэтт Ф.

Ответы:

672

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

Обратите внимание, что последнее 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)для aAFTER c. Ибо dраньше bвы бы сделали array_move(arr, 3, 1).

Reid
источник
19
Это работает отлично! И ваше объяснение очень ясно. Спасибо, что нашли время, чтобы написать это.
Марк Браун
16
Вы не должны манипулировать прототипами Object и Array, это вызывает проблемы при итерации элементов.
Бурак Эмре
9
@burakemre: я думаю, что этот вывод не так однозначно достигнут. Большинство хороших JS-программистов (и большинство популярных библиотек) будут использовать .hasOwnPropertyпроверку при выполнении итераций с такими вещами, как for..in, особенно с такими библиотеками, как Prototype и MooTools, которые модифицируют прототипы. Во всяком случае, я не чувствовал, что это было особенно важной проблемой в сравнительно ограниченном примере, подобном этому, и в сообществе есть хороший раскол по поводу того, является ли модификация прототипа хорошей идеей. Тем не менее, проблемы итераций, как правило, меньше всего волнуют.
Рейд
3
Нет необходимости в цикле на шаге 1, вы можете просто использовать его this[new_index] = undefined;внутри ifблока. Поскольку массивы Javascript редки, это увеличит размер массива, добавив new_index для .spliceработы, но без необходимости создания каких-либо промежуточных элементов.
Майкл
3
@Michael: Хорошая мысль - но если this[new_index] = undefinedвы сделаете это, то поместите undefinedв слот массива перед правильным индексом. (Например, [1,2,3].move(0,10)будет 1в слоте 10 и undefinedв слоте 9.) Скорее, если разреженность в порядке, мы могли бы обойтись this[new_index] = this.splice(old_index, 1)[0]без другого вызова соединения (вместо этого сделайте это if / else).
Рейд
268

Вот один лайнер, который я нашел на 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;
    }
  }

Я не могу взять кредит, все это должно пойти к Ричарду Скарротту . В этом тесте производительности он превосходит метод на основе сплайсинга для небольших наборов данных . Однако, как указывает Дарвейн, он значительно медленнее для больших наборов данных .

digiguru
источник
2
Ваше более эффективное решение медленнее для больших наборов данных. jsperf.com/array-prototype-move/8
Дарвейн
44
Это похоже на действительно глупую сделку. Производительность на небольших наборах данных - незначительный выигрыш, но потеря на больших наборах данных - значительная потеря. Ваш чистый обмен отрицательный.
Kyeotic
3
@Reid Это не было требованием. IMO можно предположить, что длина массива не изменяется.
robsch
3
Одно линейное решение должно обрабатывать две ситуации:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Роб Л
13
Пожалуйста, никогда не модифицируйте встроенные прототипы. nczonline.net/blog/2010/03/02/…
LJHarb
231

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

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

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

Запустите Snippet на jsFiddle

SteakOverflow
источник
29
Поскольку Array.splice возвращает удаленные значения в новом массиве, вы можете записать его как один слой ... arr.splice (index + 1, 0, arr.splice (index, 1) [0]);
Eric
49
Лично я предпочитаю 3-х строчный код. Это легче понять: получить копию элемента; удалить его из массива; вставьте его в новую позицию. Один вкладыш короче, но не настолько понятен для понимания другими людьми ...
Филипп
2
Короткий и простой код. Но это 2019 год! Создайте клон массива и верните его вместо изменения массива. Это позволит вашей функции «arraymove» соответствовать стандартам функционального программирования
SamwellTarly
36

Метод 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"]

поскольку функция является цепной, это тоже работает:

alert(arr.move(0,2).join(','));

демо здесь


источник
Есть ли библиотека, которая использует это? Довольно аккуратно!
Uicoded
Смотрите другие комментарии по этому поводу: плохая идея модифицировать встроенные прототипы, такие как Array и Object. Вы сломаете вещи.
геоидезическая
27

Мой 2с. Легко читается, работает, работает быстро, не создает новых массивов.

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;
}
Merc
источник
2
Сначала нужно вернуть строку функции array, как это было сделано в конце.
Сергей Воронежский
3
Правда, как я это пропустил? Исправлена!
Merc
Мне нравится ваше простое и гибкое решение. Спасибо!
Роман М. Косс
18

Получил эту идею от @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);
}
Анураг
источник
1
Это выглядит многообещающе ... и я не знал этого о сравнениях javascript с js. Спасибо!
Марк Браун
Не работает для дела sourceIndex = 0,destIndex = 1
Сергей Воронежский
destIndexдолжен быть индексом перед перемещением исходного элемента в массиве.
Анураг
Это лучший ответ на данный момент. Другие ответы не
Илья Иванов
16

Это основано на решении @ 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);
    });
});
Андре Пена
источник
это неправильно, если вы вставите позицию поста, индекс изменится, так как вы удалили элемент
Яо Чжао
Спасибо. Я хотел удалить элемент из массива, не оставляя нулевого элемента (что произошло при использовании splice (indexToRemove). Я использовал ваш метод для перемещения элемента, который хотел удалить, в конец массива, а затем использовал pop () метод для удаления.
Люк Шон
понравилась функция "переместить элемент в крайнее правое положение", полезная для моего случая. thx
bFunc
11

Вот мое однострочное решение 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вы хотите переместиться.

Эли Тейсседу
источник
Решение в порядке. Однако при развертывании прототипа не следует использовать функцию стрелки, поскольку в этом случае «this» - это не экземпляр массива, а, например, объект Window.
Wawka
7

spliceМетод Arrayможет помочь: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Просто имейте в виду, что это может быть относительно дорого, поскольку он должен активно переиндексировать массив.

Кен франкейро
источник
Да, но как только я выполню сращивание, индексы массива обновляются, что затрудняет мне определение места размещения только что удаленного элемента. Тем более, что мне нужна функция, чтобы можно было обрабатывать движения в обоих направлениях.
Марк Браун
@Mark: не склеивать строку и сохранять ее в той же переменной, создать новую строку и склеить ее. Смотрите мой ответ ниже.
Джаред Апдайк
7

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

пример

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']
Джаред Апдайк
источник
1
Вы понимаете, что arr2из-за операций конкатенации вы становитесь строкой, верно? :) Это в конечном итоге "adc,de".
Кен Франкейру
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

Andrea
источник
1
Это должен быть правильный ответ, поскольку он не выделяет никаких новых массивов. Спасибо!
Cᴏʀʏ
Ссылка не работает.
Рокит
6

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

Имена переменных могут быть сокращены, просто используйте длинные, чтобы код мог объяснить сам себя.

Барри Майкл Дойл
источник
безусловно, лучший ответ, мутации создают побочные эффекты
Мэтт Ло
1
Из любопытства, почему бы просто не вернуться arrayсразу же fromIndex === toIndex, а создать только, newArrayесли это не так? Неизменность не означает, что для каждого вызова функции должна быть создана одна свежая копия, даже если нет изменений. Просто спросив b / c, мотив для увеличения длины этой функции (по сравнению с однострочными строками на основе сплайсинга) - это производительность, и она fromIndexможет часто равняться в toIndexзависимости от использования.
Роберт Монфера
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, показывающий это в действии .

Джавид Джамае
источник
Всегда приятно видеть, как люди учитывают мутации.
Хуман Аскари
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

Артур Цидкилов
источник
2

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

Использование некоторых других методов при перемещении объектов на небольшие расстояния было значительно быстрее (х10), чем при сращивании. Это может измениться в зависимости от длины массива, но это верно для больших массивов.

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

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

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

BernieSF
источник
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)

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

Роберт Монфера
источник
1

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;
    }
});
Джонатан Нил
источник
2
Хотя .moveпохоже, что он должен работать (я не проверял его), вы должны заметить, что он не является частью какого-либо стандарта. Также полезно предупредить людей, что функции polyfill / monkeypatched могут сломать некоторый код, который предполагает, что все перечисляемое принадлежит им.
Джереми Дж. Старчер
1
a = ["a", "b", "c"]; a.move (0,1,1); // a = ["a", "b", "c"], должно быть ["b", "a", "c"]
Леонард Паули
2
Эта функция устарела и может больше не поддерживаться. Будьте осторожны, см .: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Мостафа
1

Я использовал хороший ответ @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
    };
Марсель Беттхер
источник
1

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

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

Диди
источник
Да - поскольку отрицательные индексы поддерживаются, на мой взгляд, имеет смысл оборачивать слишком большие индексы, а не вставлять неопределенные значения.
python1981
1

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)

Shijo Rs
источник
1

Я думал, что это проблема обмена, но это не так. Вот мое однострочное решение:

const move = (arr, from, to) => arr.map((item, i) => i === to ? arr[from] : (i >= Math.min(from, to) && i <= Math.max(from, to) ? arr[i + Math.sign(to - from)] : item));

Вот небольшой тест:

let test = ['a', 'b', 'c', 'd', 'e'];
console.log(move(test, 0, 2)); // [ 'b', 'c', 'a', 'd', 'e' ]
console.log(move(test, 1, 3)); // [ 'a', 'c', 'd', 'b', 'e' ]
console.log(move(test, 2, 4)); // [ 'a', 'b', 'd', 'e', 'c' ]
console.log(move(test, 2, 0)); // [ 'c', 'a', 'b', 'd', 'e' ]
console.log(move(test, 3, 1)); // [ 'a', 'd', 'b', 'c', 'e' ]
console.log(move(test, 4, 2)); // [ 'a', 'b', 'e', 'c', 'd' ]
console.log(move(test, 4, 0)); // [ 'e', 'a', 'b', 'c', 'd' ]
cagdas_ucar
источник
Ну, вопрос был не в обмене предметами. Автор попросил решение для стратегии вставки.
Андреас Долк
Что касается рассматриваемого вопроса, это объективно неправильный ответ.
Бен Стюард
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"]
Naycho334
источник
0

    let oldi, newi, arr;
    
    if(newi !== oldi) {
      let el = this.arr.splice(oldi, 1);
      if(newi > oldi && newi === (this.arr.length + 2)) {
        this.arr.push("");
      }
      this.arr.splice(newi, 0, el);
      if(newi > oldi && newi === (this.arr.length + 2)) {
        this.arr.pop();
      }
    }

Behnam
источник
1
Добро пожаловать на ТАК! Есть 21 дополнительный ответ ... так что, пожалуйста, не просто указывайте код. Объясните пользу своего ответа.
Дэвид Гарсия Бодего
0

var ELEMS = ['a', 'b', 'c', 'd', 'e'];
/*
    Source item will remove and it will be placed just after destination
*/
function moveItemTo(sourceItem, destItem, elements) {
    var sourceIndex = elements.indexOf(sourceItem);
    var destIndex = elements.indexOf(destItem);
    if (sourceIndex >= -1 && destIndex > -1) {
        elements.splice(destIndex, 0, elements.splice(sourceIndex, 1)[0]);
    }
    return elements;
}
console.log('Init: ', ELEMS);
var result = moveItemTo('a', 'c', ELEMS);
console.log('BeforeAfter: ', result);

Мохд Абдул Баки
источник
0

Неизменяемая версия без копии массива:

const moveInArray = (arr, fromIndex, toIndex) => {
  if (toIndex === fromIndex || toIndex >= arr.length) return arr;

  const toMove = arr[fromIndex];
  const movedForward = fromIndex < toIndex;

  return arr.reduce((res, next, index) => {
    if (index === fromIndex) return res;
    if (index === toIndex) return res.concat(
      movedForward ? [next, toMove] : [toMove, next]
    );

    return res.concat(next);
  }, []);
};
VoloshinS
источник
0

Я думаю, что лучший способ - это определить новое свойство для массивов.

Object.defineProperty(Array.prototype, 'move', {
    value: 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) {
            let k = new_index - this.length;
            while ((k--) + 1) {
                this.push(undefined);
            }
        }
        this.splice(new_index, 0, this.splice(old_index, 1)[0]);
        return this;
    }
});

console.log([10, 20, 30, 40, 50].move(0, 1));  // [20, 10, 30, 40, 50]
console.log([10, 20, 30, 40, 50].move(0, 2));  // [20, 30, 10, 40, 50]
iProDev
источник
0

Другой чистый вариант 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))
      
 

абр
источник
0

Этот метод сохранит исходный массив и проверит наличие ошибок ограничения.

const move = (from, to, arr) => {
    to = Math.max(to,0)
    from > to 
        ? [].concat(
            arr.slice(0,to), 
            arr[from], 
            arr.filter((x,i) => i != from).slice(to)) 
        : to > from
            ? [].concat(
                arr.slice(0, from), 
                arr.slice(from + 1, to + 1), 
                arr[from], 
                arr.slice(to + 1))
            : arr}
Никк Вонг
источник