Javascript swap array elements

228

Existe-t-il un moyen plus simple d'échanger deux éléments dans un tableau?

var a = list[x], b = list[y];
list[y] = a;
list[x] = b;
ken
la source

Réponses:

412

Vous n'avez besoin que d'une variable temporaire.

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

Modifier la meilleure réponse de détournement 10 ans plus tard avec beaucoup d'adoption ES6 sous nos ceintures:

Étant donné le tableau arr = [1,2,3,4], vous pouvez maintenant échanger des valeurs sur une seule ligne comme suit:

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

Cela produirait le tableau [2,1,3,4]. C'est une mission destructurante .

tvanfosson
la source
2
Même sans utiliser ECMAScript 6 Destructuring Assignment, peut en fait réaliser un échange simultané sans polluer la portée actuelle avec une variable temporaire: a = [b, b = a][0];comme souligné par @Jan Bien que je continue à utiliser l'approche de variable temporaire car elle est cross-language (par exemple C / C ++ ) et la première approche qui me vient habituellement à l'esprit.
Ultimater
3
Vous pouvez échanger en place (muter) avec es6 comme d'autres le montrent ci-dessous:[ list[y], list[x] ] = [ list[x], list[y] ];
protoEvangelion
[arr[0], arr[1]] = [arr[1], arr[0]]produire uniquement [2, 1]sans le reste de la gamme
Yerko Palma
8
@YerkoPalma - l'expression renvoie [2,1], mais le tableau d'origine sera muté en [2,1,3,4]
danbars
111

Si vous voulez une seule expression, en utilisant du javascript natif, n'oubliez pas que la valeur de retour d'une opération d'épissage contient le ou les éléments qui ont été supprimés.

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"

Éditer:

Le [0]est nécessaire à la fin de l'expression as Array.splice()renvoie un tableau, et dans cette situation, nous avons besoin de l'élément unique dans le tableau renvoyé.

kennebec
la source
3
splice renvoie un tableau. Ainsi, dans votre exemple, après l'opération de swap, votre tableau ressemble en fait à: [[2], 1, 3, 4, 5, 6, 7, 8, 9]
JPot
1
A [x] = A.plice (y, 1, A [x]) [0]; ? dans mootools Array.implement ({swap: function (x, y) {this [y] = this.splice (x, 1, this [y]) [0];}});
ken
Confirmé, le [0] est manquant.
Johann Philipp Strathausen
agréable et court, mais comme @aelgoa l'a dit, échange presque lent puis simple
ofir_aghai
75

Cela semble ok ....

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

Howerver utilisant

var b = list[y];

signifie un b variable va être présente pour le reste de la portée. Cela peut potentiellement entraîner une fuite de mémoire. Peu probable, mais encore mieux à éviter.

Peut-être une bonne idée de mettre cela dans Array.prototype.swap

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

qui peut être appelé comme:

list.swap( x, y )

Il s'agit d'une approche propre à la fois pour éviter les fuites de mémoire et DRY .

Stefan
la source
J'aime ça aussi. Array.implement ({swap: function (x, y) {x = this [x]; this [x] = this [y]; this [y] = x; return this;}});
ken
1
C'est sympa. Peut-être que certaines limites vérifient? 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. La vérification des limites est superflue et inutile. L'appelant a tout le nécessaire pour effectuer une telle vérification si cela est souhaité, bien que dans la plupart des cas, vous sachiez déjà que x et y sont liés parce que vous êtes dans une boucle quelconque.
Neil
6
Ne pourriez-vous pas éviter la "fuite de mémoire potentielle" en l'enveloppant simplement dans une fonction?
Carcigenicate
3
Pour éviter le problème potentiel "d'aplatissement", je ne toucherais à la chaîne prototype d'aucun type intégré.
AaronDancer
55

Selon une personne aléatoire sur Metafilter , "Les versions récentes de Javascript vous permettent de faire des échanges (entre autres) beaucoup plus proprement:"

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

Mes tests rapides ont montré que ce code Pythonic fonctionne très bien dans la version de JavaScript actuellement utilisée dans "Google Apps Script" (".gs"). Hélas, d'autres tests montrent que ce code donne un "Uncaught ReferenceError: côté gauche non valide dans l'affectation". quelle que soit la version de JavaScript (".js") est utilisée par Google Chrome version 24.0.1312.57 m.

David Cary
la source
2
Cela fait partie de la proposition ES6: il n'est pas encore formalisé, il ne faut donc pas absolument supposer qu'il fonctionne partout (ce serait génial si c'était le cas ...).
Isiah Meadows
2
Il fonctionne dans la dernière version actuelle de Firefox (39.0.3).
Jamie
2
Il fonctionne dans la version Chrome 54.0.2840.71 et les versions antérieures. En outre, cela devrait être votre code d'accès si vous utilisez un transpilateur ES6 tel que babel .
amoebe
3
J'adore cette solution. Propre, comme prévu. Dommage que la question ait été posée il y a 9 ans ...
DavidsKanal
2
il a été normalisé dans es6 et cette fonctionnalité est appelée déstructuration.
AL-zami
29

Eh bien, vous n'avez pas besoin de mettre en mémoire tampon les deux valeurs - une seule:

var tmp = list[x];
list[x] = list[y];
list[y] = tmp;
Marc Gravell
la source
13
votre «tmp» semble plus raisonnable à utiliser que «b»
mtasic85
@ofir_aghai oui, vous avez raison: il y a 10+ ans, une autre réponse a été postée 22 secondes avant celle-ci (12: 14: 16Z vs 12: 14: 38Z) ...
Marc Gravell
en jour normal, je m'en tenais à ça. mais juste parce que le deuxième numéro et le respect de vos 10 ans reprennent ici ;-)
ofir_aghai
désolé, il ne me permet pas de modifier le vote .. "Votre vote est désormais bloqué sauf si cette réponse est modifiée"
ofir_aghai
22

Vous pouvez échanger des éléments dans un tableau de la manière suivante:

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

Voir l'exemple suivant:

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

Remarque: cela fonctionne de la même manière pour les variables régulières

var a=1,b=5;
a = [b,b=a][0]
Jan
la source
6
Ceci est étonnamment similaire à la bonne façon standard pour le faire dans ES6 (prochaine version de JavaScript): [list[x], list[y]] = [list[y], list[x]];.
Isiah Meadows
1
Cela n'a rien à voir avec l'échange de tableaux ES6 par déstructuration. Ceci est juste une utilisation intelligente du flux de travail JS. Un beau modèle d'échange si vous utilisez fréquemment le codage en ligne, commethis[0] > this[1] && (this[0] = [this[1],this[1]=this[0]][0]);
Redu
18

Avec les valeurs numériques, vous pouvez éviter une variable temporaire en utilisant xor au niveau du bit

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

ou une somme arithmétique (en notant que cela ne fonctionne que si x + y est inférieur à la valeur maximale pour le type de données)

list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];
Jakub Arnold
la source
2
Est-ce que c'est Dark Vador? +1
krosenvold
7
Quelque chose ne va pas. Ne correspond pas list[y] = list[x] - list[x];seulement à list[y] = 0;?
ErikE
3
L'astuce xor échoue également lorsque x = y - elle met la liste [x] à zéro, alors que vous pouvez vous attendre à ce qu'elle garde la liste [x] à la valeur d'origine.
David Cary
1
Techniquement, vous créez une valeur temporaire que vous ne déplacez pas en dehors de la zone concernée du tableau.
Mark Smit
1
Ni plus simple, ni plus efficace, ni générique.
LoganMzz
17

Cela n'existait pas lorsque la question a été posée, mais ES2015 a introduit la déstructuration des baies, vous permettant de l'écrire comme suit:

let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1
dirkdig
la source
14
Pour échanger comme ceci à l'intérieur du tableau:[list[x], list[y]] = [list[y], list[x]];
Stromata
15

Pour échanger deux éléments consécutifs d'un tableau

array.splice(IndexToSwap,2,array[IndexToSwap+1],array[IndexToSwap]);
Piyush Madan
la source
13

Résumé de 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
la source
1
Si vous enveloppez cela dans une swap(a, b)fonction, vous n'avez pas à vous soucier de la lisibilité.
AccidentalTaylorExpansion
1
Fonctionne uniquement pour les entiers
Redu
Cela optimise probablement mal. Un compilateur peut le détecter comme un "idiome de swap", mais il ne peut pas être sûr des effets à moins qu'il ne puisse être sûr que les deux types sont des entiers et qu'ils ne sont pas alias .
mwfearnley
10

Qu'en est-il de la destruction_assignation

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

qui peut également être étendu à

[src order elements] => [dest order elements]
ROROROOROROR
la source
9

Considérez une telle solution sans avoir besoin de définir la troisième variable:

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

Remarque: vous souhaiterez peut-être ajouter des vérifications supplémentaires, par exemple pour la longueur du tableau. Cette solution est modifiable , la swapfonction n'a donc pas besoin de renvoyer un nouveau tableau, elle effectue simplement une mutation sur le tableau passé.

Shevchenko Viktor
la source
En complément, l'opérateur de spread pourrait également être utilisé:arr.splice(from, 1, arr.splice(to, 1, ...arr[from]))
Orkun Tuzel
7

Vous pouvez échanger n'importe quel nombre d'objets ou de littéraux, même de différents types, à l'aide d'une simple fonction d'identité comme celle-ci:

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

Pour votre problème:

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

Cela fonctionne en JavaScript car il accepte des arguments supplémentaires même s'ils ne sont pas déclarés ou utilisés. Les affectations, a=betc., se produisent après leur apassage dans la fonction.

dansalmo
la source
Hackish ... mais vous pouvez faire un mieux, si vous utilisez uniquement la fonction une fois: list[y] = (function(x){return x})(list[x],list[x]=list[y]);. Ou, si vous êtes intéressé à ES6 (prochaine version de JS), il est incroyablement facile: [list[x], list[y]] = [list[y], list[x]. Je suis tellement content qu'ils ajoutent des aspects plus fonctionnels et basés sur les classes dans la prochaine version de JavaScript.
Isiah Meadows
6

Pour deux éléments ou plus (nombre fixe)

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

Aucune variable temporaire requise!

Je pensais simplement appeler list.reverse().
Mais j'ai réalisé que cela ne fonctionnerait comme échange que lorsquelist.length = x + y + 1 .

Pour un nombre variable d'éléments

J'ai examiné différentes constructions Javascript modernes à cet effet, y compris Map et map , mais malheureusement, aucune n'a abouti à un code plus compact ou plus rapide que cette construction à l'ancienne en boucle:

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
la source
5

Il existe une façon intéressante de permuter:

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

(Manière ES6)

Vivek
la source
5
pour un tableau, c'est plusvar a= [7,8,9,10], i=2, j=3;[a[i],a[j]] = [a[j],a[i]];
caub
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
la source
3

Voici une doublure qui ne mute pas list:

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

(Utilise les fonctionnalités linguistiques non disponibles en 2009 lorsque la question a été publiée!)

fmg
la source
1

Voici une version compacte échange la valeur à i1 avec i2 en arr

arr.slice(0,i1).concat(arr[i2],arr.slice(i1+1,i2),arr[i1],arr.slice(i2+1))
user2044802
la source
C'est moins efficace que la méthode des variables temporaires. Vous retournez effectivement un tableau modifié qui a été découpé trois fois et concaténé avec deux objets entre les trois tableaux découpés. Vous avez effectivement requis plus de deux fois la mémoire que nécessaire pour obtenir la valeur à affecter simplement au tableau (rien de tout cela n'a été fait sur place).
Isiah Meadows
1

Voici une variante qui vérifie d'abord si l'index existe dans le tableau:

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

Actuellement, il reviendra simplement thissi l'index n'existe pas, mais vous pouvez facilement modifier le comportement en cas d'échec

Douglas.Sesar
la source
1

Échangez le premier et le dernier élément d'un tableau sans variable temporaire ou méthode d'échange ES6 [a, b] = [b, a]

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

gengns
la source
1

Solution dactylographiée qui clone le tableau au lieu de muter celui existant

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
la source
0

Juste pour le plaisir, une autre façon sans utiliser de variable supplémentaire serait:

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
la source
0

Par souci de concision, voici la version laide à une ligne qui n'est que légèrement moins laide que tout ce qui est concaténé et tranché ci-dessus. La réponse acceptée est vraiment la voie à suivre et bien plus lisible.

Donné:

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

si vous souhaitez échanger les valeurs de deux indices (a et b); alors cela le ferait:

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

Par exemple, si vous souhaitez échanger les 3 et 5, vous pouvez le faire de cette façon:

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

ou

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

Les deux donnent le même résultat:

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

#splicehatersarepunks :)

Jasonovich
la source
0

Si vous ne souhaitez pas utiliser la variable temp dans ES5, c'est une façon d'échanger les éléments du tableau.

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
la source
De cette façon, au lieu de créer une variable temporaire, vous créez 2 nouveaux tableaux comme a.splicerenvoie un tableau avec les éléments supprimés. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
XCS
Y a-t-il un moyen de mieux le faire? @Cristy
venkat7668
La réponse acceptée est simple. Ce sera pratique lorsque vous avez une limitation du nombre de déclarations de variables (principalement à des fins d'entretien :)). Mais pas efficace en mémoire comme vous l'avez mentionné. @Cristy
venkat7668
Personnellement, je pense que c'est une mauvaise pratique et ne devrait pas être recommandé aux débutants. C'est aussi très difficile à lire.
XCS
0

essayez cette fonction ...

$(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
la source
0

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

JATIN KUMAR NAYAK
la source
1
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
Alessio
-1

Utilisation d' ES6 il est possible de le faire comme ceci ...

Imaginez que vous ayez ces 2 tableaux ...

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

et vous voulez échanger les premières valeurs:

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

et valeur:

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

Usage:

var myArray = [0,1,2,3,4...];
myArray.swap(4,1);
user2472643
la source
1
Pas besoin d'être grossier. De plus, l'extension du Arrayprototype ne faisait pas partie de ce qui était demandé - cela peut confondre plus que bon.
Mathias Lykkegaard Lorenzen
Comment exprimer que certaines des réponses sont folles et étendre le prototype de la baie serait et ajouter un retour cela le rendrait capable de chaîner ...
user2472643
2
Vous l'exprimez comme "la bonne façon". Cela peut donner une mauvaise impression. Au lieu de cela, je suggérerais de mentionner ce que vous faites (étendre le prototype) et comment il est utile, exactement comme vous venez de me le décrire.
Mathias Lykkegaard Lorenzen
1
gotcha, désolé mon équilibre n'est pas sur la paire parfois ^ _ ^
user2472643
2
Vous êtes le seul à décrire un problème avec le contexte de la réponse ... tout d'abord, les scores négatifs doivent être réservés aux réponses qui ne fonctionnent pas. Deuxièmement, c'est une bonne réponse avec une utilisation élégante qui ne provoque aucun conflit. Jugez le code et non la livraison. Aussi dans ma réponse si vous la supprimez et excluez l'extension du prototype, elle devient exactement la même que la réponse la plus votée, donc le fait que ce soit -6 montre le manque de réflexion des gens qui l'ont voté. Et a été publié des mois avant la première réponse ... donc cela ressemble à une popularité et non à un concours de code.
user2472643
-3

Si vous souhaitez permuter uniquement les premiers et derniers éléments:

array.unshift( array.pop() );
Alex Moonlight
la source
Ce code est défectueux. Il prend le dernier élément du tableau puis le place au début, ce n'est pas un échange. Ce code fait ceci: [1, 2, 3] => [3, 1, 2]au lieu de [1, 2, 3] => [3, 2, 1].
David Archibald