Переміщення елемента масиву з одного положення масиву в інше


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

Це просто замінює елементи на initі target.
Мет Ф.

Відповіді:


671

Якщо вам потрібна версія npm, переміщення масиву є найближчим до цієї відповіді, хоча це не та сама реалізація. Докладніше див. У розділі його використання. Попередню версію цієї відповіді (що модифікував 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).


19
Це прекрасно працює! І ваше пояснення дуже зрозуміле. Дякуємо, що знайшли час, щоб написати це.
Марк Браун

16
Не слід маніпулювати прототипами Object та Array, це спричиняє проблеми при ітерації елементів.
burak emre

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

Я не можу брати жодної кредити, все це повинно йти до Річарда Скарротта . У цьому тесті на ефективність він обходиться методом на основі сплайсингу для менших наборів даних . Однак , наголошує Дарвейн, це значно повільніше на великих наборах даних .


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

230

Мені це подобається. Це стисло і це працює.

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

Примітка. Не забудьте перевірити межі масиву.

Запустіть фрагмент на jsFiddle


29
Оскільки Array.splice повертає видалені значення (и) у новому масиві, ви можете записати його як один вкладиш ... arr.splice (індекс + 1, 0, arr.splice (індекс, 1) [0]);
Ерік

49
Особисто я віддаю перевагу коду 3 рядка. Простіше зрозуміти: отримати копію елемента; видаліть його з масиву; вставити його в нове положення. Один лайнер коротший, але не так зрозумілий, щоб інші люди зрозуміли ...
Філіпп

2
Короткий і простий код. Але це 2019 рік !!, Створіть клон масиву і поверніть його замість того, щоб мутувати мульт. Це змусить вашу функцію "arraymove" відповідати стандартам функціонального програмування
SamwellTarly

4
Гаразд, але не все не є і не повинно відповідати функціональному програмуванню; плюс це все ще може бути корисним у функціональному програмуванні всередині процедури для маніпулювання локальними масивами.
SteakOverflow

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

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

це неправильно, якщо ви вставите посаду посади, індекс зміниться, оскільки ви видалите цей предмет
Яо Чжао

Дякую. Я хотів вилучити елемент з масиву, не залишаючи нульового елемента (який стався при використанні сплайсу (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ви хочете переміститись.


Розчин прекрасний. Однак, коли ви розгортаєте прототип, ви не повинні використовувати функцію стрілки, оскільки в цьому випадку "це" не є екземпляром масиву, а, наприклад, об'єктом Window.
wawka

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

1
Ви усвідомлюєте, що ваші arr2закінчення є рядком через операцій зв'язання, правда? :) Це закінчується буттям "adc,de".
Кен Франкейро

6

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

Пам'ятайте, що це може бути досить дорогим, оскільки він повинен активно переіндексувати масив.


Так, але як тільки я виконую сплайс, індекси масиву оновлюються, що ускладнює мені зрозуміти, де розмістити елемент, який я щойно видалив. Тим більше, що мені потрібна функція, щоб можна було обробляти рухи в обох напрямках.
Марк Браун

@Mark: не зробіть рядок і не збережіть його в одній змінній, зробіть нову рядок і зробіть її. Дивіться мою відповідь нижче.
Jared Updike

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


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, що показує це в дії .


Завжди добре бачити ppl з урахуванням мутацій.
Hooman Askari

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

Я в кінцевому підсумку поєднав два з них, щоб трохи краще працювати при переміщенні і малих, і великих відстаней. Я отримую досить послідовні результати, але це, мабуть, могло б трохи змінитись хтось розумніший за мене, щоб працювати по-різному для різних розмірів тощо.

Використання деяких інших методів при переміщенні об'єктів на невеликі відстані було значно швидше (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

У багатьох місцях це зазначається ( додавання користувальницьких функцій у 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

Надія може бути корисною будь-кому


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

Поліфіл

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виглядає, що він повинен працювати (я його не перевіряв), ви повинні зауважити, що він не входить до жодного стандарту. Також добре попередити людей про те, що функції «заповнення полів» / «мавпа» можуть порушити якийсь код, який передбачає, що все перелічене є їхнім.
Jeremy J Starcher

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)


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

Ну, питання було не в заміні предметів. Автор попросив вирішити стратегію вставки.
Андреас Долк

Що стосується розглянутого питання, це об'єктивно неправильна відповідь.
Бен Стюард

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

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


1
Ласкаво просимо до SO! Є ще 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);
  }, []);
};

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]

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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.