Елементи масиву підкачки Javascript


228

Чи є простіший спосіб замінити два елементи в масиві?

var a = list[x], b = list[y];
list[y] = a;
list[x] = b;

Відповіді:


411

Вам потрібна лише одна тимчасова змінна.

var b = list[y];
list[y] = list[x];
list[x] = b;

Редагуйте головну відповідь про викрадення через 10 років, і багато підкреслили ES6 під нашими поясами:

Враховуючи масив arr = [1,2,3,4], тепер ви можете поміняти значення в одному рядку так:

[arr[0], arr[1]] = [arr[1], arr[0]];

Це створило б масив [2,1,3,4]. Це руйнування завдання .


2
Навіть не використовуючи ECMAScript 6 Destructuring Assignment, можна реально домогтися одночасного заміни, не забруднюючи поточний діапазон тимчасовою змінною: на a = [b, b = a][0];що вказує @Jan Хоча я все ще можу використовувати тимчасовий змінний підхід як його мовний переклад (наприклад, C / C ++ ) і перший підхід, який зазвичай вискакує мені на думку.
Ultimater

3
Ви можете поміняти місцями (мутувати) на es6, як показують інші:[ list[y], list[x] ] = [ list[x], list[y] ];
protoEvangelion

[arr[0], arr[1]] = [arr[1], arr[0]]виробляти лише [2, 1]без решти масиву
Yerko Palma

8
@YerkoPalma - вираз повертається [2,1], але початковий масив буде змінено на [2,1,3,4]
danbars

111

Якщо ви хочете отримати одне вираження, використовуючи нативний javascript, пам’ятайте, що повернене значення в результаті операції на сплайс містить елементи, які були видалені.

var A = [1, 2, 3, 4, 5, 6, 7, 8, 9], x= 0, y= 1;
A[x] = A.splice(y, 1, A[x])[0];
alert(A); // alerts "2,1,3,4,5,6,7,8,9"

Редагувати:

[0]Необхідно в кінці виразу , як Array.splice()повертає масив, і в цій ситуації , ми вимагаємо , щоб один елемент в повернутому масиві.


3
splice повертає масив. Отже, у вашому прикладі після операції swap ваш масив насправді виглядає так: [[2], 1, 3, 4, 5, 6, 7, 8, 9]
JPot

1
A [x] = A.splice (y, 1, A [x]) [0]; ? у mootools Array.implement ({swap: function (x, y) {this [y] = this.splice (x, 1, this [y]) [0];}});
кен

Підтверджено, що [0] відсутній.
Йоганн Філіп Стратхаузен

приємно і коротко, але як сказав @aelgoa, майже повільний, а потім простий своп
ofir_aghai

75

Це здається нормальним ....

var b = list[y];
list[y] = list[x];
list[x] = b;

Ховервер за допомогою

var b = list[y];

означає, що змінна b буде присутня для решти області. Це потенційно може призвести до витоку пам'яті. Навряд чи, але все ж краще уникати.

Можливо, гарна ідея помістити це в Array.prototype.swap

Array.prototype.swap = function (x,y) {
  var b = this[x];
  this[x] = this[y];
  this[y] = b;
  return this;
}

який можна назвати так:

list.swap( x, y )

Це чіткий підхід до того, щоб уникнути витоку пам'яті та СУХОГО .


Мені це теж подобається. Array.implement ({swap: function (x, y) {x = this [x]; this [x] = this [y]; this [y] = x; return this;}});
кен

1
Це добре. Можливо, деякі межі перевіряють? Array.prototype.swap = function (x,y) { if (x >= 0 && x < this.length && y >= 0 && y < this.length) { var b = this[x]; this[x] = this[y]; this[y] = b; } return this; };
Девід Р.

@DavidR. Перевірка меж є зайвою і непотрібною. Викликаючий має все необхідне для здійснення такої перевірки, якщо це бажано, хоча в більшості випадків ви вже знаєте, що x і y знаходяться в межах, тому що ви знаходитесь у певній петлі.
Ніл

6
Не могли б ви уникнути "потенційного витоку пам'яті", просто увімкнувши його у функцію?
Carcigenicate

3
Щоб уникнути можливих "згладжених" випадкових випадків, я б не торкався прототипу ланцюга будь-яких вбудованих типів.
AaronDancer

54

За словами деяких випадкових людей на Metafilter , "Останні версії Javascript дозволяють робити свопи (серед іншого) набагато акуратніше:"

[ list[x], list[y] ] = [ list[y], list[x] ];

Мої швидкі тести показали, що цей Pythonic-код чудово працює у версії JavaScript, яка зараз використовується у "Google Apps Script" (".gs"). На жаль, подальші тести показують, що цей код дає "Uncaught ReferenceError: Недійсний лівий бік у призначенні". у будь-якій версії JavaScript (".js") використовується Google Chrome Версія 24.0.1312.57 m.


2
Це частина пропозиції ES6: вона ще не формалізована, тому не слід абсолютно вважати, що вона працює скрізь (було б дивним, якби це було ...).
Ісія Медоуз

2
Він працює в останній версії Firefox (39.0.3).
Джеймі

2
Він працює у версії Chrome 54.0.2840.71 та більш ранніх версіях. Крім того, це повинен бути ваш код коду, якщо ви використовуєте транспілятор ES6, такий як babel .
амебе

3
Любіть це рішення. Очистіть за призначенням. Шкода, що запитання було задано 9 років тому ...
DavidsKanal

2
він був стандартизований в es6, і ця особливість називається руйнуванням.
AL-zami

29

Ну, вам не потрібно буферувати обидва значення - лише одне:

var tmp = list[x];
list[x] = list[y];
list[y] = tmp;

13
ваш 'tmp' звучить більш розумним, а потім 'b'
mtasic85

@ofir_aghai так, ти маєш рацію: 10+ років тому ще одна відповідь була опублікована за 22 секунди до цієї (12: 14: 16Z проти 12: 14: 38Z) ...
Marc Gravell

в звичайний день, я дотримувався цього. але тільки тому, що секунди випуску та поваги ваших 10 років поновлюються тут ;-)
ofir_aghai

вибачте, що я не дозволяю мені змінити голосування. "Ваш голос заблокований, якщо ця відповідь не буде відредагована"
ofir_aghai

22

Ви можете змінювати елементи в масиві наступним чином:

list[x] = [list[y],list[y]=list[x]][0]

Дивіться наступний приклад:

list = [1,2,3,4,5]
list[1] = [list[3],list[3]=list[1]][0]
//list is now [1,4,3,2,5]

Примітка. Це працює аналогічно для звичайних змінних

var a=1,b=5;
a = [b,b=a][0]

6
Це разюче схожий на стандартний правильний спосіб зробити це в ES6 (наступна версія JavaScript) [list[x], list[y]] = [list[y], list[x]];.
Ісія Медоуз

1
Це не має нічого спільного з тим, щоб ми не обмінювались масивами ES6 шляхом деструктурування. Це просто розумне використання робочого процесу JS. Гарний шаблон заміни, якщо ви часто використовуєте вбудоване кодування, наприклад,this[0] > this[1] && (this[0] = [this[1],this[1]=this[0]][0]);
зменшити

18

За допомогою числових значень можна уникнути тимчасової змінної, використовуючи розрядний xor

list[x] = list[x] ^ list[y];
list[y] = list[y] ^ list[x];
list[x] = list[x] ^ list[y];

або арифметична сума (зауваживши, що це працює лише в тому випадку, якщо x + y менше максимального значення для типу даних)

list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];

2
Це дарт, як у відер? +1
krosenvold

7
Щось не так. Це не list[y] = list[x] - list[x];просто прирівнюється до list[y] = 0;?
ЕрікЕ

3
Трюк xor також не вдається, коли x = y - він встановлює список [x] до нуля, коли ви можете очікувати, що він збереже список [x] початкового значення.
Девід Кері

1
Технічно ви створюєте тимчасове значення, просто не пересуваєте його за межі відповідної області масиву.
Марк Сміт

1
Ні простішим, ні більш ефективним, ні загальним.
LoganMzz

17

Цього не було, коли було задано питання, але ES2015 запровадив деструктуризацію масиву, дозволяючи записати його так:

let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1

14
Помінятись таким чином всередині масиву:[list[x], list[y]] = [list[y], list[x]];
Stromata

15

Для заміни двох послідовних елементів масиву

array.splice(IndexToSwap,2,array[IndexToSwap+1],array[IndexToSwap]);

13

Дайджест з http://www.greywyvern.com/?post=265

var a = 5, b = 9;    
b = (a += b -= a) - b;    
alert([a, b]); // alerts "9, 5"

1
Якщо ви завершите цю swap(a, b)функцію, вам не потрібно буде турбуватися про читабельність.
AccidentalTaylorExpansion

1
Працює лише для цілих чисел
зменшити

Це, ймовірно, оптимізує погано. Компілятор може визначити це як «ідіому підкачки», але не може бути впевнений у ефектах, якщо він не може бути впевнений, що обидва типи є ints, а також що вони не мають псевдоніму .
mwfearnley


9

Розглянемо таке рішення без необхідності визначення третьої змінної:

function swap(arr, from, to) {
  arr.splice(from, 1, arr.splice(to, 1, arr[from])[0]);
}

var letters = ["a", "b", "c", "d", "e", "f"];

swap(letters, 1, 4);

console.log(letters); // ["a", "e", "c", "d", "b", "f"]

Примітка. Ви можете додати додаткові перевірки, наприклад, для довжини масиву. Це рішення є змінним, тому swapфункції не потрібно повертати новий масив, він просто робить мутацію над переданим масивом.


Як додаток, також може бути використаний оператор розповсюдження:arr.splice(from, 1, arr.splice(to, 1, ...arr[from]))
Orkun Tuzel

7

Ви можете поміняти місцями будь-яку кількість об'єктів або літералів, навіть різних типів, використовуючи просту функцію ідентичності на зразок цієї:

var swap = function (x){return x};
b = swap(a, a=b);
c = swap(a, a=b, b=c);

Для вашої проблеми:

var swap = function (x){return x};
list[y]  = swap(list[x], list[x]=list[y]);

Це працює в JavaScript, оскільки він приймає додаткові аргументи, навіть якщо вони не оголошені або використані. Присвоєння a=bтощо відбувається після того, як aбуде передано функцію.


Хак ... але ви могли б зробити один краще, якщо ви тільки з допомогою функції один раз: list[y] = (function(x){return x})(list[x],list[x]=list[y]);. Або, якщо ви зацікавлені в ES6 (наступна версія JS), це шалено легко: [list[x], list[y]] = [list[y], list[x]. Я радий, що вони додають ще кілька функціональних та на основі класів аспектів у наступну версію JavaScript.
Ісія Медоуз

6

Для двох або більше елементів (фіксований номер)

[list[y], list[x]] = [list[x], list[y]];

Не потрібна тимчасова змінна!

Я думав про те, щоб просто подзвонити list.reverse().
Але тоді я зрозумів, що це буде працювати як своп лише тоді, коли list.length = x + y + 1.

Для змінної кількості елементів

Я подивився в різних сучасних Javascript конструкцій для цієї мети, в тому числі карти і карти , але , до жаль , ніхто не привів в коді , який був більш компактним або швидше , ніж це старомодно, будівництво петлі на основі:

function multiswap(arr,i0,i1) {/* argument immutable if string */
    if (arr.split) return multiswap(arr.split(""), i0, i1).join("");
    var diff = [];
    for (let i in i0) diff[i0[i]] = arr[i1[i]];
    return Object.assign(arr,diff);
}

Example:
    var alphabet = "abcdefghijklmnopqrstuvwxyz";
    var [x,y,z] = [14,6,15];
    var output = document.getElementsByTagName("code");
    output[0].innerHTML = alphabet;
    output[1].innerHTML = multiswap(alphabet, [0,25], [25,0]);
    output[2].innerHTML = multiswap(alphabet, [0,25,z,1,y,x], [25,0,x,y,z,3]);
<table>
    <tr><td>Input:</td>                        <td><code></code></td></tr>
    <tr><td>Swap two elements:</td>            <td><code></code></td></tr>
    <tr><td>Swap multiple elements:&nbsp;</td> <td><code></code></td></tr>
</table>


5

Є один цікавий спосіб заміни:

var a = 1;
var b = 2;
[a,b] = [b,a];

(ES6 спосіб)


5
для масиву - це більшеvar a= [7,8,9,10], i=2, j=3;[a[i],a[j]] = [a[j],a[i]];
кают


3

Ось один вкладиш, який не мутує list:

let newList = Object.assign([], list, {[x]: list[y], [y]: list[x]})

(Використовується мовна функція, недоступна в 2009 році, коли було розміщено питання!)


1

Ось компактна версія замінює значення на i1 на i2 в arr

arr.slice(0,i1).concat(arr[i2],arr.slice(i1+1,i2),arr[i1],arr.slice(i2+1))

Це менш ефективний метод тимчасової змінної. Ви ефективно повертаєте модифікований масив, який був нарізаний тричі та об'єднаний разом з двома об'єктами між трьома нарізаними масивами. Вам фактично потрібно більше, ніж удвічі більше пам’яті, ніж потрібно, щоб отримати значення, щоб просто призначити масив (нічого з цього не було зроблено на місці).
Ісія Медоуз

1

Ось варіант, який спочатку перевіряє, чи існує індекс у масиві:

Array.prototype.swapItems = function(a, b){
    if(  !(a in this) || !(b in this) )
        return this;
    this[a] = this.splice(b, 1, this[a])[0];
    return this;
}

Наразі він просто повернеться, thisякщо індекс не існує, але ви можете легко змінити поведінку на помилку


1

Обміняйте перший і останній елемент у масиві без тимчасової змінної або методу swap ES6 [a, b] = [b, a]

[a.pop(), ...a.slice(1), a.shift()]


1

Рішення Typescript, яке клонує масив замість мутування існуючого

export function swapItemsInArray<T>(items: T[], indexA: number, indexB: number): T[] {
  const itemA = items[indexA];

  const clone = [...items];

  clone[indexA] = clone[indexB];
  clone[indexB] = itemA;

  return clone;
}

0

Для задоволення це ще одним способом без використання зайвої змінної буде:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

// swap index 0 and 2
arr[arr.length] = arr[0];   // copy idx1 to the end of the array
arr[0] = arr[2];            // copy idx2 to idx1
arr[2] = arr[arr.length-1]; // copy idx1 to idx2
arr.length--;               // remove idx1 (was added to the end of the array)


console.log( arr ); // -> [3, 2, 1, 4, 5, 6, 7, 8, 9]


0

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

Подано:

var foo = [ 0, 1, 2, 3, 4, 5, 6 ];

якщо ви хочете поміняти місцями значення двох індексів (a і b); то це зробить це:

foo.splice( a, 1, foo.splice(b,1,foo[a])[0] );

Наприклад, якщо ви хочете поміняти місцями 3 і 5, ви можете зробити це так:

foo.splice( 3, 1, foo.splice(5,1,foo[3])[0] );

або

foo.splice( 5, 1, foo.splice(3,1,foo[5])[0] );

Обидва дають однаковий результат:

console.log( foo );
// => [ 0, 1, 2, 5, 4, 3, 6 ]

#splicehatersarepunks :)


0

Якщо ви не хочете використовувати змінну temp в ES5, це один із способів поміняти елементи масиву.

var swapArrayElements = function (a, x, y) {
  if (a.length === 1) return a;
  a.splice(y, 1, a.splice(x, 1, a[y])[0]);
  return a;
};

swapArrayElements([1, 2, 3, 4, 5], 1, 3); //=> [ 1, 4, 3, 2, 5 ]

Таким чином, замість того, щоб створювати змінну temp, ви створюєте два нових масиви, як a.spliceповертає масив із видаленими елементами. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
XCS

Чи є спосіб, що ми можемо це зробити краще? @Cristy
venkat7668

Прийнята відповідь прямо-таки вперед. Це стане в нагоді, коли у вас є обмеження щодо кількості оголошень змінних (в основному, ціль інтерв'ю :)). Але не ефективна пам'ять, як ви згадали. @Cristy
venkat7668

Я особисто вважаю, що це погана практика, і її не слід рекомендувати новачкам. Це також дуже важко читати.
XCS

0

спробуйте цю функцію ...

$(document).ready(function () {
        var pair = [];
        var destinationarray = ['AAA','BBB','CCC'];

        var cityItems = getCityList(destinationarray);
        for (var i = 0; i < cityItems.length; i++) {
            pair = [];
            var ending_point = "";
            for (var j = 0; j < cityItems[i].length; j++) {
                pair.push(cityItems[i][j]);
            }
            alert(pair);
            console.log(pair)
        }

    });
    function getCityList(inputArray) {
        var Util = function () {
        };

        Util.getPermuts = function (array, start, output) {
            if (start >= array.length) {
                var arr = array.slice(0);
                output.push(arr);
            } else {
                var i;

                for (i = start; i < array.length; ++i) {
                    Util.swap(array, start, i);
                    Util.getPermuts(array, start + 1, output);
                    Util.swap(array, start, i);
                }
            }
        }

        Util.getAllPossiblePermuts = function (array, output) {
            Util.getPermuts(array, 0, output);
        }

        Util.swap = function (array, from, to) {
            var tmp = array[from];
            array[from] = array[to];
            array[to] = tmp;
        }
        var output = [];
        Util.getAllPossiblePermuts(inputArray, output);
        return output;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


0

var arr = [1, 2];
arr.splice(0, 2, arr[1], arr[0]);
console.log(arr); //[2, 1]


1
Хоча цей фрагмент коду може вирішити питання, у тому числі пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
Алессіо

-1

За допомогою ES6 це можливо зробити так ...

Уявіть, у вас є ці 2 масиви ...

const a = ["a", "b", "c", "d", "e"];
const b = [5, 4, 3, 2, 1];

і ви хочете поміняти місцями перші значення:

const [a0] = a;
a[0] = b[0];
b[0] = a0;

і значення:

a; //[5, "b", "c", "d", "e"]
b; //["a", 4, 3, 2, 1]

-2
Array.prototype.swap = function(a, b) {
  var temp = this[a];
  this[a] = this[b];
  this[b] = temp;
};

Використання:

var myArray = [0,1,2,3,4...];
myArray.swap(4,1);

1
Не потрібно бути грубим. Крім того, розширення Arrayпрототипу не було частиною того, що просили - це може заплутати більше, ніж приносить користь.
Матіас Ліккегор Лоренцен

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

2
Ви висловлюєте це як "правильний шлях". Це може створити неправильне враження. Натомість я б запропонував згадати, що ви робите (розширюючи прототип), і як це корисно, саме так, як ви мені щойно описали.
Mathias Lykkegaard Lorenzen

1
gotcha, вибачте, що моя
душа

2
Ви єдиний описуєте проблему з контекстом відповіді ... спочатку негативні бали повинні бути зарезервовані для непрацюючих відповідей. По-друге, це гарна відповідь з елегантним використанням, яке не викликає конфліктів. Судіть про код, а не про доставку. Також у моїй відповіді, якщо ви позбавили його і виключили розширення прототипу, він стає таким же, як і відповідь, який найбільше проголосував, тому факт, що це -6, свідчить про відсутність думки у людей, які його проголосували. І була розміщена за місяці до основної відповіді ... тому це звучить як популярність, а не конкурс коду.
user2472643

-3

Якщо потрібно поміняти місцями лише перший та останній елементи:

array.unshift( array.pop() );

Цей код є несправним. Він бере останній елемент масиву, а потім ставить його на початку, що не змінюється. Цей код робить так: [1, 2, 3] => [3, 1, 2]замість [1, 2, 3] => [3, 2, 1].
Девід Арчібальд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.