Elementy tablicy wymiany JavaScript

228

Czy istnieje prostszy sposób na zamianę dwóch elementów w tablicy?

var a = list[x], b = list[y];
list[y] = a;
list[x] = b;
rozpoznać
źródło

Odpowiedzi:

411

Potrzebujesz tylko jednej zmiennej tymczasowej.

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

Edytuj najlepszą odpowiedź na temat porwania 10 lat później, wprowadzając wiele ES6 pod nasze pasy:

Biorąc pod uwagę tablicę arr = [1,2,3,4], możesz teraz zamieniać wartości w jednym wierszu w następujący sposób:

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

Spowoduje to utworzenie tablicy [2,1,3,4]. To jest zadanie restrukturyzacji .

tvanfosson
źródło
2
Nawet bez korzystania z przypisania Destrukturyzacji ECMAScript 6 można faktycznie osiągnąć jednoczesną zamianę bez zanieczyszczania bieżącego zakresu zmienną tymczasową: a = [b, b = a][0];jak wskazał @Jan. Mimo to wciąż używam podejścia do zmiennych tymczasowych, ponieważ jest ono wielojęzyczne (np. C / C ++ ) i pierwsze podejście, które zwykle przychodzi mi na myśl.
Ultimater
3
Możesz zamieniać się (mutować) za pomocą es6, jak pokazano poniżej:[ list[y], list[x] ] = [ list[x], list[y] ];
ProtoEvangelion
[arr[0], arr[1]] = [arr[1], arr[0]]produkować tylko [2, 1]bez reszty zestawu
Yerko Palma
8
@YerkoPalma - wyrażenie zwraca [2,1], ale pierwotna tablica zostanie zmutowana do [2,1,3,4]
danbars
111

Jeśli chcesz użyć pojedynczego wyrażenia, używając natywnego języka JavaScript, pamiętaj, że wartość zwracana z operacji łączenia zawiera elementy, które zostały usunięte.

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"

Edytować:

Jest [0]to konieczne na końcu wyrażenia, ponieważ Array.splice()zwraca tablicę, aw tej sytuacji wymagamy pojedynczego elementu w zwróconej tablicy.

Kennebec
źródło
3
splice zwraca tablicę. Tak więc w twoim przykładzie po operacji wymiany tablica wygląda następująco: [[2], 1, 3, 4, 5, 6, 7, 8, 9]
JPot
1
A [x] = A.splice (y, 1, A [x]) [0]; ? in mootools Array.implement ({swap: function (x, y) {this [y] = this.splice (x, 1, this [y]) [0];}});
Ken
Potwierdzono, brak [0].
Johann Philipp Strathausen
ładne i krótkie, ale jak powiedział @aelgoa, prawie wolna, a potem prosta zamiana
ofir_aghai
75

To wydaje się ok ....

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

Howerver używa

var b = list[y];

oznacza b że do końca zakresu będzie obecna zmienna . Może to potencjalnie doprowadzić do wycieku pamięci. Mało prawdopodobne, ale nadal lepiej go unikać.

Może dobrym pomysłem jest umieszczenie tego w Array.prototype.swap

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

które można nazwać:

list.swap( x, y )

Jest to czyste podejście zarówno do unikania wycieków pamięci, jak i DRY .

Stefan
źródło
Ja też to lubię. Array.implement ({swap: function (x, y) {x = this [x]; this [x] = this [y]; this [y] = x; return this;}});
ken
1
To jest miłe. Może sprawdzanie granic? 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; };
David R.
@DavidR. Sprawdzanie granic jest zbędne i niepotrzebne. Osoba dzwoniąca ma wszystko, co konieczne, aby wykonać taką kontrolę, jeśli jest to pożądane, chociaż w większości przypadków już wiesz, że xiy są w granicach, ponieważ jesteś w jakiejś pętli.
Neil,
6
Czy nie można uniknąć „potencjalnego wycieku pamięci”, po prostu zawijając go w funkcji?
Carcigenicate
3
Aby uniknąć potencjalnej wpadki „spłaszczenia”, nie dotknąłbym łańcucha prototypów jakichkolwiek wbudowanych typów.
AaronDancer
54

Według pewnej przypadkowej osoby z Metafilter , „Najnowsze wersje Javascript pozwalają na zamianę (między innymi) o wiele ładniej:”

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

Moje szybkie testy wykazały, że ten kod Python działa świetnie w wersji JavaScript używanej obecnie w „Google Apps Script” („.gs”). Niestety, dalsze testy pokazują, że ten kod daje komunikat „Nieprzechwycony błąd referencyjny: Nieprawidłowe przypisanie lewej strony”. w dowolnej wersji JavaScript („.js”) używanej przez Google Chrome w wersji 24.0.1312.57 m.

David Cary
źródło
2
Jest to część propozycji ES6: nie jest jeszcze sformalizowana, dlatego nie należy absolutnie zakładać, że będzie działać wszędzie (byłoby świetnie, gdyby to zrobiło ...).
Isiah Meadows
2
Działa w aktualnej najnowszej wersji Firefoksa (39.0.3).
Jamie,
2
Działa w wersji Chrome 54.0.2840.71 i wcześniejszych wersjach. Powinien to być również Twój kod, jeśli używasz transpilatora ES6, takiego jak babel .
amoebe
3
Uwielbiam to rozwiązanie. Czyste, zgodnie z przeznaczeniem. Szkoda, że ​​pytanie zostało zadane 9 lat temu ...
DavidsKanal
2
został znormalizowany w es6 i ta funkcja nazywa się destrukcją.
AL-zami
29

Nie musisz buforować obu wartości - tylko jedna:

var tmp = list[x];
list[x] = list[y];
list[y] = tmp;
Marc Gravell
źródło
13
twój „tmp” wydaje się bardziej rozsądny w użyciu niż „b”
mtasic85
@ofir_aghai tak, masz rację: 10+ lat temu, inna odpowiedź została opublikowana 22 sekundy przed tym (12: 14: 16Z vs. 12: 14: 38Z) ...
Marc Gravell
w zwykły dzień trzymałem się tego. ale tylko dlatego, że wznowiono tutaj kwestię sekund i szacunek z 10 lat ;-)
ofir_aghai
przepraszam, nie pozwala mi to zmienić głosu. „Twój głos jest teraz zablokowany, chyba że zostanie edytowana ta odpowiedź”
ofir_aghai
22

Możesz zamieniać elementy w tablicy w następujący sposób:

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

Zobacz następujący przykład:

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

Uwaga: działa tak samo dla zmiennych regularnych

var a=1,b=5;
a = [b,b=a][0]
Jan
źródło
6
To jest uderzająco podobny do standardowego prawidłowy sposób to zrobić w ES6 (następna wersja JavaScript) [list[x], list[y]] = [list[y], list[x]];.
Isiah Meadows
1
Nie ma to nic wspólnego z zamianą macierzy ES6 poprzez dezorganizację. To tylko sprytne wykorzystanie przepływu pracy JS. Piękny wzór zamiany, jeśli często używasz kodowania wbudowanego, takiego jakthis[0] > this[1] && (this[0] = [this[1],this[1]=this[0]][0]);
Redu
18

Za pomocą wartości liczbowych można uniknąć zmiennej tymczasowej, stosując xor bitowy

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

lub suma arytmetyczna (zauważając, że działa to tylko wtedy, gdy x + y jest mniejsza niż maksymalna wartość dla typu danych)

list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];
Jakub Arnold
źródło
2
Czy to darth jak w vaderze? +1
krosenvold
7
Coś jest nie tak. To się nie list[y] = list[x] - list[x];równa list[y] = 0;?
ErikE,
3
Sztuczka xor również kończy się niepowodzeniem, gdy x = y - ustawia listę [x] na zero, gdy można oczekiwać, że zachowa oryginalną wartość na liście [x].
David Cary
1
Technicznie tworzysz wartość tymczasową, po prostu nie przenosisz jej poza odpowiedni obszar tablicy.
Mark Smit
1
Ani prostsze, ani bardziej wydajne, ani ogólne.
LoganMzz,
17

Nie istniało to, gdy pytanie zostało zadane, ale ES2015 wprowadził destrukcję tablicy, umożliwiając zapisanie jej w następujący sposób:

let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1
dirkdig
źródło
14
Aby zamienić w ten sposób w tablicy:[list[x], list[y]] = [list[y], list[x]];
Stromata
15

Aby zamienić dwa kolejne elementy tablicy

array.splice(IndexToSwap,2,array[IndexToSwap+1],array[IndexToSwap]);
Piyush Madan
źródło
13

Podsumowanie ze strony http://www.greywyvern.com/?post=265

var a = 5, b = 9;    
b = (a += b -= a) - b;    
alert([a, b]); // alerts "9, 5"
R-way Orz
źródło
1
Jeśli otoczysz to swap(a, b)funkcją, nie musisz się martwić o czytelność.
AccidentalTaylorExpansion
1
Działa tylko dla liczb całkowitych
Redu
Prawdopodobnie źle się to optymalizuje. Kompilator może wykryć to jako „idiom wymiany”, ale nie może być pewien efektów, chyba że może mieć pewność, że oba typy są liczbami całkowitymi, a także że nie są one aliasami .
mwfearnley
10

co z podziałem_rozwoju

var arr = [1, 2, 3, 4]
[arr[index1], arr[index2]] = [arr[index2], arr[index1]]

który można również rozszerzyć na

[src order elements] => [dest order elements]
ROROROOROROR
źródło
9

Rozważ takie rozwiązanie bez potrzeby definiowania trzeciej zmiennej:

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

Uwaga: Możesz chcieć dodać dodatkowe kontrole, na przykład dla długości tablicy. To rozwiązanie jest modyfikowalne, więc swapfunkcja nie musi zwracać nowej tablicy, po prostu dokonuje mutacji w stosunku do przekazanej tablicy.

Szewczenko Wiktor
źródło
Jako dodatek można również zastosować operator rozprzestrzeniania:arr.splice(from, 1, arr.splice(to, 1, ...arr[from]))
Orkun Tuzel,
7

Możesz zamienić dowolną liczbę obiektów lub literałów, nawet różnego rodzaju, używając prostej funkcji tożsamości, takiej jak ta:

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

Twój problem:

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

Działa to w JavaScript, ponieważ akceptuje dodatkowe argumenty, nawet jeśli nie są zadeklarowane ani użyte. Przydziały a=bitp. Zdarzają się po aprzekazaniu do funkcji.

dansalmo
źródło
Hackish ... ale można zrobić jeden lepiej, jeśli tylko przy użyciu funkcji raz: list[y] = (function(x){return x})(list[x],list[x]=list[y]);. Lub, jeśli jesteś zainteresowany w ES6 (następna wersja JS), to jest szalenie proste: [list[x], list[y]] = [list[y], list[x]. Cieszę się, że dodają bardziej funkcjonalne i oparte na klasach aspekty do następnej wersji JavaScript.
Isiah Meadows
6

Dla dwóch lub więcej elementów (stała liczba)

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

Nie jest wymagana zmienna tymczasowa!

Myślałem o zwykłym dzwonieniu list.reverse().
Ale potem zdałem sobie sprawę, że to zadziała jako zamiana tylko wtedy, gdylist.length = x + y + 1 .

Dla zmiennej liczby elementów

W tym celu przyjrzałem się różnym współczesnym konstrukcjom JavaScript, w tym Map i Map , ale niestety żadna z nich nie zaowocowała kodem, który byłby bardziej zwarty lub szybszy niż ta staromodna konstrukcja oparta na pętli:

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>

7vujy0f0hy
źródło
5

Jest jeden interesujący sposób zamiany:

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

(Sposób ES6)

Vivek
źródło
5
jeśli chodzi o tablicę, to więcejvar a= [7,8,9,10], i=2, j=3;[a[i],a[j]] = [a[j],a[i]];
2015
4
var a = [1,2,3,4,5], b=a.length;

for (var i=0; i<b; i++) {
    a.unshift(a.splice(1+i,1).shift());
}
a.shift();
//a = [5,4,3,2,1];
Nathan Romano
źródło
3

Oto linijka, która nie mutuje list:

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

(Wykorzystuje funkcje językowe niedostępne w 2009 roku, kiedy pytanie zostało opublikowane!)

fmg
źródło
1

Oto kompaktowa wersja zamienia wartość na i1 z i2 w arr

arr.slice(0,i1).concat(arr[i2],arr.slice(i1+1,i2),arr[i1],arr.slice(i2+1))
użytkownik2044802
źródło
Jest to mniej wydajne niż metoda zmiennej tymczasowej. Skutecznie zwracasz zmodyfikowaną tablicę, która została pocięta trzykrotnie i połączona razem z dwoma obiektami między trzema wyciętymi tablicami. Skutecznie potrzebujesz więcej niż dwa razy więcej pamięci niż to konieczne, aby uzyskać wartość do przypisania do tablicy (żadna z tych czynności nie została wykonana na miejscu).
Isiah Meadows
1

Oto odmiana, która najpierw sprawdza, czy indeks istnieje w tablicy:

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

Obecnie zwróci tylko, thisjeśli indeks nie istnieje, ale można łatwo zmodyfikować zachowanie w przypadku niepowodzenia

Douglas.Sesar
źródło
1

Zamień pierwszy i ostatni element w tablicy bez zmiennej tymczasowej lub metody zamiany ES6 [a, b] = [b, a]

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

gengns
źródło
1

Rozwiązanie maszynopisu, które klonuje tablicę zamiast mutować istniejącą

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;
}
pie6k
źródło
0

Dla zabawy, innym sposobem bez użycia dodatkowej zmiennej byłoby:

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]

vsync
źródło
0

Dla zwięzłości, oto brzydka wersja jednoliniowa, która jest tylko nieco mniej brzydka niż wszystkie te konkat i krojenie powyżej. Przyjęta odpowiedź jest naprawdę drogą i jest bardziej czytelna.

Dany:

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

jeśli chcesz zamienić wartości dwóch indeksów (a i b); to by to zrobiło:

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

Na przykład, jeśli chcesz zamienić 3 i 5, możesz to zrobić w ten sposób:

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

lub

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

Oba dają ten sam wynik:

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

#splicehatersarepunks :)

Jasonowicz
źródło
0

Jeśli nie chcesz używać zmiennej temp w ES5, jest to jeden ze sposobów zamiany elementów tablicy.

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 ]
venkat7668
źródło
W ten sposób zamiast tworzyć zmienną temp, tworzysz 2 nowe tablice, ponieważ a.splicezwraca tablicę z usuniętymi elementami. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
XCS
Czy jest jakiś sposób, abyśmy mogli to zrobić lepiej? @Cristy
venkat7668
Zaakceptowana odpowiedź jest prosta. Będzie to przydatne, gdy będziesz mieć ograniczoną liczbę deklaracji zmiennych (głównie cel wywiadu :)). Ale nie wydajna pamięć, jak wspomniałeś. @Cristy
venkat7668
Osobiście uważam, że jest to zła praktyka i nie powinna być polecana początkującym. Jest również bardzo trudny do odczytania.
XCS
0

wypróbuj tę funkcję ...

$(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>

bilal chaudhari
źródło
0

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

JATIN KUMAR NAYAK
źródło
1
Ten fragment kodu może rozwiązać pytanie, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie dla czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
Alessio
-1

Za pomocą ES6 można to zrobić w ten sposób ...

Wyobraź sobie, że masz te 2 tablice ...

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

i chcesz zamienić pierwsze wartości:

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

i wartość:

a; //[5, "b", "c", "d", "e"]
b; //["a", 4, 3, 2, 1]
Alireza
źródło
-2
Array.prototype.swap = function(a, b) {
  var temp = this[a];
  this[a] = this[b];
  this[b] = temp;
};

Stosowanie:

var myArray = [0,1,2,3,4...];
myArray.swap(4,1);
użytkownik2472643
źródło
1
Nie musisz być niegrzeczny. Ponadto rozszerzenie Arrayprototypu nie było częścią tego, o co prosiło - może bardziej pomieszać, niż zrobić.
Mathias Lykkegaard Lorenzen
Jak wyrażanie, że niektóre odpowiedzi są szalone i rozszerzenie prototypu tablicy byłoby, a dodanie zwrotu sprawiłoby, że łańcuch byłby w stanie ...
user2472643,
2
Wyrażasz to jako „właściwy sposób”. Może sprawiać złe wrażenie. Zamiast tego proponuję wspomnieć o tym, co robisz (rozbudowa prototypu) i o tym, jak to jest użyteczne, dokładnie tak, jak właśnie mi to opisałeś.
Mathias Lykkegaard Lorenzen
1
gotcha, przepraszam, mój
zestaw
2
Jesteś jedynym, który opisuje problem z kontekstem odpowiedzi ... najpierw negatywne wyniki powinny być zarezerwowane dla niedziałających odpowiedzi. Po drugie, jest to dobra odpowiedź z eleganckim użyciem, które nie powoduje konfliktów. Oceniaj kod, a nie dostawę. Również w mojej odpowiedzi, jeśli ją rozebrałeś i wykluczyłeś rozszerzenie prototypu, staje się on dokładnie taki sam, jak najczęściej głosowana odpowiedź, więc fakt, że jest to -6, pokazuje brak myślenia ze strony osób, które głosowały za nią. I został opublikowany na kilka miesięcy przed najważniejszą odpowiedzią ... więc brzmi to jak popularność, a nie konkurs kodowy.
user2472643,
-3

W razie potrzeby zamień tylko pierwszy i ostatni element:

array.unshift( array.pop() );
Alex Moonlight
źródło
Ten kod jest wadliwy. Bierze ostatni element tablicy, a następnie umieszcza go na początku, co nie jest zamianą. Ten kod robi to: [1, 2, 3] => [3, 1, 2]zamiast [1, 2, 3] => [3, 2, 1].
David Archibald,