Elementos de matriz de troca de Javascript

228

Existe alguma maneira mais simples de trocar dois elementos em uma matriz?

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

Respostas:

412

Você só precisa de uma variável temporária.

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

Edite a resposta principal do seqüestro 10 anos depois com muita adoção do ES6 em nossos cintos:

Dada a matriz arr = [1,2,3,4], você pode trocar valores em uma linha agora da seguinte forma:

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

Isso produziria a matriz [2,1,3,4]. Esta é uma tarefa de desestruturação .

tvanfosson
fonte
2
Mesmo sem utilizar a atribuição de desestruturação do ECMAScript 6, é possível obter uma troca simultânea sem poluir o escopo atual com uma variável temporária: a = [b, b = a][0];como apontado por @Jan Embora eu ainda me encontre utilizando a abordagem de variável temporária como é uma linguagem cruzada (por exemplo, C / C ++ ) e a primeira abordagem que geralmente vem à minha mente.
Ultimater
3
Você pode trocar de lugar (mutação) com ES6 como outros mostram a seguir:[ list[y], list[x] ] = [ list[x], list[y] ];
protoEvangelion
[arr[0], arr[1]] = [arr[1], arr[0]]produzir apenas [2, 1], sem o resto da matriz
Yerko Palma
8
@YerkoPalma - a expressão retorna [2,1], mas a matriz original será mutado para [2,1,3,4]
danbars
111

Se você deseja uma única expressão, usando javascript nativo, lembre-se de que o valor de retorno de uma operação de emenda contém os elementos que foram removidos.

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"

Editar:

O [0]é necessário no final da expressão, pois Array.splice()retorna uma matriz e, nessa situação, exigimos o elemento único na matriz retornada.

Kennebec
fonte
3
splice retorna uma matriz. Assim, no seu exemplo, depois da operação de permuta de sua matriz realmente se parece com: [[2], 1, 3, 4, 5, 6, 7, 8, 9]
JPot
1
A [x] = A.splice (y, 1, A [x]) [0]; ? em mootools Array.implement ({swap: function (x, y) {this [y] = this.splice (x, 1, this [y]) [0];}});
ken
Confirmado, o [0] está ausente.
Johann Philipp Strathausen
agradável e curto, mas como @aelgoa disse, quase lento, em seguida, simples troca
ofir_aghai
75

Isso parece ok ....

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

Howerver usando

var b = list[y];

significa a b variável estará presente no restante do escopo. Isso pode levar a um vazamento de memória. Improvável, mas ainda melhor evitar.

Talvez seja uma boa ideia colocar isso em Array.prototype.swap

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

que pode ser chamado como:

list.swap( x, y )

Essa é uma abordagem limpa para evitar vazamentos de memória e DRY .

Stefan
fonte
Eu também gosto disso. Array.implement ({swap: function (x, y) {x = this [x]; this [x] = this [y]; this [y] = x; return this;}});
Ken
1
Isso é legal. Talvez alguns limites de verificação? 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. A verificação de limites é supérflua e desnecessária. O chamador tem todo o necessário para realizar essa verificação, se desejado, embora na maioria dos casos você já saiba que xey estão dentro dos limites porque você está em algum tipo de loop.
Neil
6
Você não poderia evitar o "vazamento de memória em potencial" apenas envolvendo-o em uma função?
Carcigenicate
3
Para evitar o possível acidente "achatado", não tocaria na cadeia de protótipos de nenhum tipo interno.
AaronDancer
54

De acordo com uma pessoa aleatória no Metafilter , "As versões recentes do Javascript permitem fazer trocas (entre outras coisas) de maneira muito mais clara:"

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

Meus testes rápidos mostraram que esse código Pythonic funciona muito bem na versão do JavaScript atualmente usada no "Script do Google Apps" (".gs"). Infelizmente, testes adicionais mostram que esse código fornece um "Erro de referência não detectado: lado esquerdo inválido na atribuição". em qualquer versão do JavaScript (".js") usada pelo Google Chrome versão 24.0.1312.57 m.

David Cary
fonte
2
Isso faz parte da proposta do ES6: ainda não foi formalizado, portanto, não se deve absolutamente supor que funcione em todos os lugares (seria incrível se funcionasse ...).
Isiah Meadows
2
Ele funciona na versão mais recente do firefox atual (39.0.3).
Jamie
2
Ele funciona na versão 54.0.2840.71 e versões anteriores do Chrome. Além disso, esse deve ser o seu código básico se você usar um transpiler ES6 como o babel .
Amebe
3
Adoro esta solução. Limpe, como pretendido. Pena que a pergunta foi feita há 9 anos ...
DavidsKanal
2
foi padronizado no es6 e esse recurso é chamado de desestruturação.
AL-zami #
29

Bem, você não precisa armazenar os dois valores em buffer - apenas um:

var tmp = list[x];
list[x] = list[y];
list[y] = tmp;
Marc Gravell
fonte
13
seus sons 'tmp' mais razoável usar, em seguida, 'b'
mtasic85
@ofir_aghai sim, você está certo: há mais de 10 anos, outra resposta foi publicada 22 segundos antes desta (12: 14: 16Z vs 12: 14: 38Z) ...
Marc Gravell
em dia normal, eu fiquei com ela. mas apenas porque os segundos emitir e respeito de seus 10 anos de retomar aqui ;-)
ofir_aghai
desculpe, não me permita alterar o voto .. "Seu voto está bloqueado, a menos que esta resposta seja editada"
ofir_aghai
22

Você pode trocar elementos em uma matriz da seguinte maneira:

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

Veja o seguinte exemplo:

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

Nota: funciona da mesma maneira para variáveis ​​regulares

var a=1,b=5;
a = [b,b=a][0]
Jan
fonte
6
Isto é muito semelhante à maneira como padrão correto de fazer isso em ES6 (próxima versão do JavaScript): [list[x], list[y]] = [list[y], list[x]];.
Isiah Meadows
1
Isso não tem nada a ver com a troca do array ES6 pela desestruturação. Este é apenas um uso inteligente do fluxo de trabalho JS. Um padrão de swap bonita se você usar linha de codificação com freqüência, comothis[0] > this[1] && (this[0] = [this[1],this[1]=this[0]][0]);
Redu
18

Com valores numéricos, você pode evitar uma variável temporária usando xor bit a bit

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

ou uma soma aritmética (observando que isso só funciona se x + y for menor que o valor máximo para o tipo de dados)

list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];
Jakub Arnold
fonte
2
Isso é Darth como em Vader? +1
krosenvold
7
Algo está errado. Não list[y] = list[x] - list[x];equivale apenas a list[y] = 0;?
ErikE
3
O truque xor também falha quando x = y - define a lista [x] como zero, quando você pode esperar que mantenha a lista [x] o valor original.
David Cary
1
Tecnicamente, você cria um valor temporário, apenas não o move para fora da área relevante da matriz.
Mark Smit
1
Nem mais simples, nem mais eficiente, nem genérico.
LoganMzz
17

Isso não existia quando a pergunta foi feita, mas o ES2015 introduziu a desestruturação da matriz, permitindo que você a escreva da seguinte maneira:

let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1
dirkdig
fonte
14
Para trocar assim dentro da matriz:[list[x], list[y]] = [list[y], list[x]];
Stromata 22/03
15

Para trocar dois elementos consecutivos da matriz

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

Resumo 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
fonte
1
Se você envolver isso em uma swap(a, b)função, não precisará se preocupar com a legibilidade.
AccidentalTaylorExpansion
1
Funciona apenas para números inteiros
Redu
Isso provavelmente otimiza mal. Um compilador pode detectá-lo como um "idioma de troca", mas não pode ter certeza dos efeitos, a menos que possa ter certeza de que ambos os tipos são ints e também que não possuem alias .
Mwfearnley 23/09/19
10

o que dizer de Destructuring_assignment

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

que também pode ser estendido para

[src order elements] => [dest order elements]
ROROROOROROR
fonte
9

Considere essa solução sem a necessidade de definir a terceira variável:

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

Nota: convém adicionar verificações adicionais, por exemplo, para o comprimento da matriz. Esta solução é mutável, portanto, a swapfunção não precisa retornar uma nova matriz, apenas faz a mutação na matriz passada.

Shevchenko Viktor
fonte
Como um complemento, o operador de spread também pode ser usado:arr.splice(from, 1, arr.splice(to, 1, ...arr[from]))
Orkun Tuzel
7

Você pode trocar qualquer número de objetos ou literais, mesmo de tipos diferentes, usando uma função de identidade simples como esta:

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

Para o seu problema:

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

Isso funciona no JavaScript porque aceita argumentos adicionais, mesmo que não sejam declarados ou usados. As atribuições a=betc acontecem depois que asão passadas para a função.

dansalmo
fonte
Hackish ... mas você poderia fazer um melhor, se você estiver usando apenas a função uma vez: list[y] = (function(x){return x})(list[x],list[x]=list[y]);. Ou, se você estiver interessado em ES6 (próxima versão do JS), é insanamente fácil: [list[x], list[y]] = [list[y], list[x]. Estou tão feliz que eles estão adicionando alguns aspectos mais funcionais e baseados em classe na próxima versão do JavaScript.
Isiah Meadows
6

Para dois ou mais elementos (número fixo)

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

Nenhuma variável temporária é necessária!

Eu estava pensando em simplesmente ligar list.reverse().
Mas então percebi que só funcionaria como swap quando list.length = x + y + 1.

Para número variável de elementos

Analisei várias construções Javascript modernas para esse efeito, incluindo Mapa e mapa , mas infelizmente nenhuma resultou em um código mais compacto ou mais rápido do que essa construção antiquada e baseada em loop:

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
fonte
5

Há uma maneira interessante de trocar:

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

(Caminho ES6)

Vivek
fonte
5
para uma matriz, é maisvar 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
fonte
3

Aqui está uma frase que não muda list:

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

(Usa recursos de idioma não disponíveis em 2009 quando a pergunta foi publicada!)

fmg
fonte
1

Aqui está uma versão compacta que troca o valor em i1 com i2 em arr

arr.slice(0,i1).concat(arr[i2],arr.slice(i1+1,i2),arr[i1],arr.slice(i2+1))
user2044802
fonte
Isso é menos eficiente que o método variável temporário. Você está efetivamente retornando uma matriz modificada que foi fatiada três vezes e concatenada junto com dois objetos entre as três matrizes fatiadas. Você efetivamente exigiu mais que o dobro da memória do que o necessário para obter o valor simplesmente atribuído à matriz (nada disso foi feito no local).
Isiah Meadows
1

Aqui está uma variação que primeiro verifica se o índice existe na matriz:

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

Atualmente, ele retornará apenas thisse o índice não existir, mas você poderá modificar facilmente o comportamento em caso de falha

Douglas.Sesar
fonte
1

Troque o primeiro e o último elemento em uma matriz sem variável temporária ou método de troca ES6 [a, b] = [b, a]

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

gengns
fonte
1

Solução datilografada que clona a matriz em vez de alterar uma existente

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
fonte
0

Apenas por diversão, outra maneira sem usar nenhuma variável extra seria:

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
fonte
0

Por uma questão de brevidade, aqui está a versão feia de uma linha que é apenas um pouco menos feia do que todas as concat e fatias acima. A resposta aceita é realmente o caminho a percorrer e muito mais legível.

Dado:

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

se você deseja trocar os valores de dois índices (a e b); então isso faria isso:

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

Por exemplo, se você deseja trocar os 3 e 5, pode fazê-lo desta maneira:

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

ou

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

Ambos produzem o mesmo resultado:

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

#splicehatersarepunks :)

Jasonovich
fonte
0

Se você não deseja usar a variável temp no ES5, essa é uma maneira de trocar elementos da matriz.

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
fonte
Dessa forma, em vez de criar uma variável temp, você está criando 2 novas matrizes, pois a.spliceretorna uma matriz com os elementos removidos. developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
XCS
Existe alguma maneira de fazê-lo melhor? @Cristy
venkat7668 25/03/19
A resposta aceita é direta. Isso será útil quando você tiver uma limitação no número de declarações de variáveis ​​(principalmente para fins de entrevista :)). Mas não é eficiente em termos de memória, como você mencionou. @Cristy
venkat7668
Pessoalmente, acho que é uma prática ruim e não deve ser recomendada para iniciantes. Também é muito difícil de ler.
XCS
0

tente esta função ...

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

chaudhari bilateral
fonte
0

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

JATIN KUMAR NAYAK
fonte
1
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Alessio
-1

Usando o ES6 , é possível fazer assim ...

Imagine que você tem essas duas matrizes ...

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

e você deseja trocar os primeiros valores:

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

e valor:

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

Uso:

var myArray = [0,1,2,3,4...];
myArray.swap(4,1);
user2472643
fonte
1
Não precisa ser rude. Além disso, estender o Arrayprotótipo não fazia parte do que foi solicitado - ele pode confundir mais do que serve.
Mathias Lykkegaard Lorenzen
Como está expressando que algumas das respostas são loucos e estendendo o protótipo variedade seria e adicionando um retorno isso tornaria cadeia capaz ...
user2472643
2
Você está expressando como "a maneira correta". Pode dar a impressão errada. Em vez disso, sugiro mencionar o que você está fazendo (estendendo o protótipo) e como ele é útil, exatamente como você acabou de me descrever.
Mathias Lykkegaard Lorenzen
1
peguei, desculpe, meu porte não está em par às vezes ^ _ ^ #
user2472643
2
Você é o único que descreve um problema com o contexto da resposta ... pontuações negativas primeiro devem ser reservadas para respostas que não estão funcionando. Segundo, esta é uma boa resposta com um uso elegante que não causa conflitos. Julgue o código e não a entrega. Também na minha resposta, se você o retirou e excluiu a extensão do protótipo, ele se torna exatamente o mesmo que a resposta mais votada, de modo que o fato de ser -6 mostra a falta de pensamento das pessoas que votaram contra. E foi postado meses antes da resposta principal ... então isso parece uma popularidade, não um concurso de códigos.
user2472643
-3

Se for necessário trocar apenas o primeiro e o último elementos:

array.unshift( array.pop() );
Alex Moonlight
fonte
Este código é falho. Ele pega o último elemento da matriz e o coloca no início, que não está sendo trocado. Este código faz o seguinte: em [1, 2, 3] => [3, 1, 2]vez de [1, 2, 3] => [3, 2, 1].
David Archibald