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;

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 .


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.


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 .


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.


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;

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]

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

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

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

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"

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]

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.


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.


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>


5

Jest jeden interesujący sposób zamiany:

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

(Sposób ES6)


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

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


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

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


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()]


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

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]


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


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 ]

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>


0

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


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]

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

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

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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.