Mover um elemento da matriz de uma posição da matriz para outra

522

Estou tendo dificuldade para descobrir como mover um elemento da matriz. Por exemplo, considerando o seguinte:

var arr = [ 'a', 'b', 'c', 'd', 'e'];

Como posso escrever uma função para mover 'd'antes 'b'?

Ou 'a'depois 'c'?

Após a movimentação, os índices do restante dos elementos devem ser atualizados. Isso significa que no primeiro exemplo, após a movimentação, arr [0] seria = 'a', arr [1] = 'd' arr [2] = 'b', arr [3] = 'c', arr [4] = 'e'

Parece que deve ser bem simples, mas não consigo entender.

Mark Brown
fonte
3
bem esta questão de idade, mas o ouro
Jalal
usando ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa
Isso apenas troca os elementos em inite target.
Matt F.

Respostas:

671

Se você deseja uma versão no npm, a movimentação da matriz é a mais próxima dessa resposta, embora não seja a mesma implementação. Veja sua seção de uso para mais detalhes. A versão anterior desta resposta (que modificou Array.prototype.move) pode ser encontrada no npm em array.prototype.move .


Eu tive um sucesso bastante bom com esta função:

function array_move(arr, old_index, new_index) {
    if (new_index >= arr.length) {
        var k = new_index - arr.length + 1;
        while (k--) {
            arr.push(undefined);
        }
    }
    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    return arr; // for testing
};

// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1)); 

Observe que o último returné simplesmente para fins de teste: spliceexecuta operações no array no local, portanto, um retorno não é necessário. Por extensão, esta moveé uma operação no local. Se você quiser evitar isso e retornar uma cópia, use slice.

Percorrendo o código:

  1. Se new_indexfor maior que o comprimento da matriz, queremos (presumo) que a matriz seja preenchida corretamente com novos undefineds. Esse pequeno trecho lida com isso pressionando undefineda matriz até obtermos o comprimento adequado.
  2. Em seguida, arr.splice(old_index, 1)[0]dividimos o elemento antigo. spliceretorna o elemento que foi emendado, mas está em uma matriz. No exemplo acima, isso foi [1]. Então, pegamos o primeiro índice desse array para obter o raw 1lá.
  3. Então usamos splicepara inserir esse elemento no lugar do new_index. Como preenchemos a matriz acima se new_index > arr.length, provavelmente ela aparecerá no lugar certo, a menos que eles tenham feito algo estranho como passar em um número negativo.

Uma versão mais sofisticada para contabilizar índices negativos:

function array_move(arr, old_index, new_index) {
    while (old_index < 0) {
        old_index += arr.length;
    }
    while (new_index < 0) {
        new_index += arr.length;
    }
    if (new_index >= arr.length) {
        var k = new_index - arr.length + 1;
        while (k--) {
            arr.push(undefined);
        }
    }
    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    return arr; // for testing purposes
};
    
// returns [1, 3, 2]
console.log(array_move([1, 2, 3], -1, -2));

O que deve dar conta de coisas como array_move([1, 2, 3], -1, -2)corretamente (mova o último elemento para o segundo para o último lugar). O resultado para isso deve ser [1, 3, 2].

De qualquer forma, na sua pergunta original, você faria array_move(arr, 0, 2)para adepois c. Para dantes b, você faria array_move(arr, 3, 1).

Reid
fonte
19
Isso funciona perfeitamente! E sua explicação é muito clara. Obrigado por reservar um tempo para escrever isso.
Marque
16
Você não deve manipular os protótipos de Objeto e Matriz, pois isso causa problemas ao iterar elementos.
burak emre
9
@burakemre: Eu acho que essa conclusão não é tão claramente alcançada. A maioria dos bons programadores JS (e as bibliotecas mais populares) usará uma .hasOwnPropertyverificação ao iterar com coisas como for..in, especialmente com bibliotecas como Prototype e MooTools que modificam protótipos. De qualquer forma, não achei que fosse uma questão particularmente importante em um exemplo relativamente limitado como esse, e há uma boa divisão na comunidade sobre se a modificação do protótipo é ou não uma boa idéia. Normalmente, os problemas de iteração são a menor preocupação.
Reid
3
Não há necessidade do loop na etapa 1, você pode simplesmente usar this[new_index] = undefined;dentro do ifbloco. Como as matrizes Javascript são escassas, isso estenderá o tamanho da matriz para incluir o new_index para o .splicetrabalho, mas sem a necessidade de criar nenhum elemento intermediário.
Michael
3
@ Michael: Bom ponto - mas this[new_index] = undefined, na verdade, colocar um undefinedslot na matriz antes do índice correto. (Por exemplo, [1,2,3].move(0,10)terá 1no slot 10 e undefinedno slot 9.) Em vez disso, se a escassez estiver boa, poderíamos ficar this[new_index] = this.splice(old_index, 1)[0]sem a outra chamada de emenda (transformá-lo em um if / else).
Reid
268

Aqui está um liner que eu encontrei no JSPerf ....

Array.prototype.move = function(from, to) {
    this.splice(to, 0, this.splice(from, 1)[0]);
};

o que é incrível de ler, mas se você quiser desempenho (em pequenos conjuntos de dados), tente ...

 Array.prototype.move2 = function(pos1, pos2) {
    // local variables
    var i, tmp;
    // cast input parameters to integers
    pos1 = parseInt(pos1, 10);
    pos2 = parseInt(pos2, 10);
    // if positions are different and inside array
    if (pos1 !== pos2 && 0 <= pos1 && pos1 <= this.length && 0 <= pos2 && pos2 <= this.length) {
      // save element from position 1
      tmp = this[pos1];
      // move element down and shift other elements up
      if (pos1 < pos2) {
        for (i = pos1; i < pos2; i++) {
          this[i] = this[i + 1];
        }
      }
      // move element up and shift other elements down
      else {
        for (i = pos1; i > pos2; i--) {
          this[i] = this[i - 1];
        }
      }
      // put element from position 1 to destination
      this[pos2] = tmp;
    }
  }

Eu não posso aceitar nenhum crédito, tudo deve ir para Richard Scarrott . Ele supera o método baseado em emenda para conjuntos de dados menores neste teste de desempenho . No entanto, é significativamente mais lento em conjuntos de dados maiores, como aponta Darwayne .

digiguru
fonte
2
Sua solução com melhor desempenho é mais lenta em grandes conjuntos de dados. jsperf.com/array-prototype-move/8
Darwayne
44
Parece uma troca muito boba. O desempenho em pequenos conjuntos de dados é um ganho insignificante, mas a perda em grandes conjuntos de dados é uma perda significativa. Sua troca líquida é negativa.
Kyeotic
3
@ Reid Isso não era um requisito. Na IMO, não há problema em supor que o comprimento da matriz não seja modificado.
robsch
3
A solução de uma linha precisa lidar com duas situações:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L
13
Por favor, nunca modifique os protótipos incorporados. nczonline.net/blog/2010/03/02/…
LJHarb 4/18
231

Eu gosto desse jeito. É conciso e funciona.

function arraymove(arr, fromIndex, toIndex) {
    var element = arr[fromIndex];
    arr.splice(fromIndex, 1);
    arr.splice(toIndex, 0, element);
}

Nota: lembre-se sempre de verificar os limites do seu array.

Executar Snippet no jsFiddle

SteakOverflow
fonte
29
Como Array.splice retorna o (s) valor (es) removido (s) em uma nova Matriz, você pode escrevê-lo como um liner ... arr.splice (índice + 1, 0, arr.splice (índice, 1) [0]);
Eric
49
Pessoalmente, prefiro o código de 3 linhas. É mais fácil entender: obtenha uma cópia do elemento; remova-o da matriz; insira-o em uma nova posição. A um forro é mais curto, mas não tão clara para outras pessoas a entender ...
Philipp
2
Código curto e simples. Mas é 2019 !!, crie um clone da matriz e retorne-o em vez de mutá-la. Isso fará com que a sua função "arraymove" cumprir com os padrões de programação funcional
SamwellTarly
4
OK, mas nem tudo nem é nem precisa ser compatível com a programação funcional; Além disso, isso ainda pode ser útil na programação funcional dentro de um procedimento para manipular matrizes locais.
SteakOverflow 5/08/19
36

O método splice () adiciona / remove itens de / para uma matriz e retorna os itens removidos .

Nota: Este método altera a matriz original. / w3schools /

Array.prototype.move = function(from,to){
  this.splice(to,0,this.splice(from,1)[0]);
  return this;
};

var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(3,1);//["a", "d", "b", "c", "e"]


var arr = [ 'a', 'b', 'c', 'd', 'e'];
arr.move(0,2);//["b", "c", "a", "d", "e"]

como a função é encadeada, isso também funciona:

alert(arr.move(0,2).join(','));

demonstração aqui


fonte
Existe alguma biblioteca que usa isso? Muito arrumado!
Uicoded
Veja outros comentários sobre isso: é uma má idéia modificar protótipos internos como Array e Object. Você vai quebrar as coisas.
geoidesic
27

Meu 2c. Fácil de ler, funciona, é rápido, não cria novas matrizes.

function move(array, from, to) {
  if( to === from ) return array;

  var target = array[from];                         
  var increment = to < from ? -1 : 1;

  for(var k = from; k != to; k += increment){
    array[k] = array[k + increment];
  }
  array[to] = target;
  return array;
}
Merc
fonte
2
Na primeira sequência de funções, você deve retornar array, como foi feito no final.
Sergey Voronezhskiy
3
É verdade que eu senti falta disso? Fixo!
Merc
Gosto mais da sua solução simples e flexível. Valeu!
Roman M. Koss
18

Com a idéia de @Reid, tive a ideia de colocar algo no lugar do item que deveria ser movido para manter o tamanho da matriz constante. Isso simplifica os cálculos. Além disso, empurrar um objeto vazio tem os benefícios adicionais de poder procurá-lo exclusivamente posteriormente. Isso funciona porque dois objetos não são iguais até que estejam se referindo ao mesmo objeto.

({}) == ({}); // false

Então, aqui está a função que recebe a matriz de origem e a fonte, os índices de destino. Você pode adicioná-lo ao Array.prototype, se necessário.

function moveObjectAtIndex(array, sourceIndex, destIndex) {
    var placeholder = {};
    // remove the object from its initial position and
    // plant the placeholder object in its place to
    // keep the array length constant
    var objectToMove = array.splice(sourceIndex, 1, placeholder)[0];
    // place the object in the desired position
    array.splice(destIndex, 0, objectToMove);
    // take out the temporary object
    array.splice(array.indexOf(placeholder), 1);
}
Anurag
fonte
1
Isso parece promissor ... e eu não sabia disso sobre comparações de javascript js. Obrigado!
Mark Brown
Não funciona para o caso sourceIndex = 0,destIndex = 1
Sergey Voronezhskiy
destIndexdeve ser o índice antes que o elemento de origem seja movido na matriz.
Anurag
Esta é a melhor resposta até agora. Outras respostas falhou um par de testes de unidade no meu suite (avançar objeto)
Ilya Ivanov
16

Isso é baseado na solução da @ Reid. Exceto:

  • Não estou mudando o Arrayprotótipo.
  • Mover um item para fora dos limites para a direita não cria undefineditens, apenas move o item para a posição mais à direita.

Função:

function move(array, oldIndex, newIndex) {
    if (newIndex >= array.length) {
        newIndex = array.length - 1;
    }
    array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
    return array;
}

Testes unitários:

describe('ArrayHelper', function () {
    it('Move right', function () {
        let array = [1, 2, 3];
        arrayHelper.move(array, 0, 1);
        assert.equal(array[0], 2);
        assert.equal(array[1], 1);
        assert.equal(array[2], 3);
    })
    it('Move left', function () {
        let array = [1, 2, 3];
        arrayHelper.move(array, 1, 0);
        assert.equal(array[0], 2);
        assert.equal(array[1], 1);
        assert.equal(array[2], 3);
    });
    it('Move out of bounds to the left', function () {
        let array = [1, 2, 3];
        arrayHelper.move(array, 1, -2);
        assert.equal(array[0], 2);
        assert.equal(array[1], 1);
        assert.equal(array[2], 3);
    });
    it('Move out of bounds to the right', function () {
        let array = [1, 2, 3];
        arrayHelper.move(array, 1, 4);
        assert.equal(array[0], 1);
        assert.equal(array[1], 3);
        assert.equal(array[2], 2);
    });
});
André Pena
fonte
isso é errado, se você inserir uma posição post, o índice vai mudar desde que você tem remover o item
Yao Zhao
Obrigado. Eu queria remover um item de uma matriz sem deixar um elemento nulo (que ocorreu ao usar a emenda (indexToRemove) .Eu usei seu método para mover o item que eu queria remover para o final da matriz e, em seguida, usei o pop () método para remover.
Luke Schoen
gostei do recurso "mover o item para a posição mais à direita", útil para o meu caso. thx
bFunc
11

Aqui está minha solução ES6 de um liner com um parâmetro opcional on.

if (typeof Array.prototype.move === "undefined") {
  Array.prototype.move = function(from, to, on = 1) {
    this.splice(to, 0, ...this.splice(from, on))
  }
}

Adaptação da primeira solução proposta por digiguru

O parâmetro oné o número de elementos que fromvocê deseja mover.

Elie Teyssedou
fonte
A solução está boa. No entanto, ao expandir um protótipo, você não deve usar a função de seta, porque neste caso 'this' não é uma instância de matriz, mas, por exemplo, objeto Window.
Wawka 23/06/19
7

O splicemétodo de Arraypode ajudar: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Lembre-se de que pode ser relativamente caro, pois precisa re-indexar ativamente a matriz.

Ken Franqueiro
fonte
Sim, mas assim que executo a emenda, os índices da matriz são atualizados, o que dificulta para mim descobrir onde colocar o elemento que acabei de remover. Especialmente porque eu preciso da função para poder lidar com movimentos nas duas direções.
Mark Brown
@ Mark: não emendar a string e salvá-la na mesma variável, criar uma nova string e emendar isso. Veja minha resposta abaixo.
precisa
7

Uma abordagem seria criar uma nova matriz com as peças na ordem desejada, usando o método de fatia.

Exemplo

var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
  • arr.slice (0,1) fornece a você ['a']
  • arr.slice (2,4) fornece ['b', 'c']
  • arr.slice (4) fornece a você ['e']
Jared Updike
fonte
1
Você percebe que você arr2acaba sendo uma string devido às operações de concatenação, certo? :) acaba sendo "adc,de".
Ken Franqueiro 15/03
6

Você pode implementar algum cálculo básico e criar uma função universal para mover o elemento da matriz de uma posição para outra.

Para JavaScript, é assim:

function magicFunction (targetArray, indexFrom, indexTo) { 

    targetElement = targetArray[indexFrom]; 
    magicIncrement = (indexTo - indexFrom) / Math.abs (indexTo - indexFrom); 

    for (Element = indexFrom; Element != indexTo; Element += magicIncrement){ 
        targetArray[Element] = targetArray[Element + magicIncrement]; 
    } 

    targetArray[indexTo] = targetElement; 

}

Confira "mover elementos da matriz" em "gloommatter" para obter explicações detalhadas.

http://www.gloommatter.com/DDesign/programming/moving-any-array-elements-universal-function.html

Andrea
fonte
1
Essa deve ser a resposta correta, pois não aloca novas matrizes. Obrigado!
Cᴏʀʏ
O link está quebrado.
Rokit 6/02
6

Eu implementei uma ECMAScript 6solução imutável baseada na @Mercresposta de aqui:

const moveItemInArrayFromIndexToIndex = (array, fromIndex, toIndex) => {
  if (fromIndex === toIndex) return array;

  const newArray = [...array];

  const target = newArray[fromIndex];
  const inc = toIndex < fromIndex ? -1 : 1;

  for (let i = fromIndex; i !== toIndex; i += inc) {
    newArray[i] = newArray[i + inc];
  }

  newArray[toIndex] = target;

  return newArray;
};

Os nomes das variáveis ​​podem ser encurtados, apenas os longos, para que o código possa se explicar.

Barry Michael Doyle
fonte
definitivamente uma resposta melhor, mutações cria efeitos colaterais
Matt Lo
1
Por curiosidade, por que não apenas retornar arrayimediatamente se fromIndex === toIndex, e apenas criar o newArrayse não for o caso? Imutabilidade não significa que uma nova cópia deve ser criada por chamada de função, mesmo quando não há alterações. Apenas pedir a b / c o motivo para o aumento do comprimento dessa função (em relação às linhas de base baseadas em emenda) é desempenho e fromIndexpode muito bem ser igual toIndex, dependendo do uso.
Robert Monfera
5

Eu precisava de um método de movimento imutável (que não alterasse a matriz original), então adaptei a resposta aceita de @ Reid para simplesmente usar Object.assign para criar uma cópia da matriz antes de fazer a emenda.

Array.prototype.immutableMove = function (old_index, new_index) {
  var copy = Object.assign([], this);
  if (new_index >= copy.length) {
      var k = new_index - copy.length;
      while ((k--) + 1) {
          copy.push(undefined);
      }
  }
  copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
  return copy;
};

Aqui está um jsfiddle mostrando-o em ação .

Javid Jamae
fonte
É sempre bom ver pessoas levando mutações em consideração.
21917 Hooman Askari
4
    Array.prototype.moveUp = function (value, by) {
        var index = this.indexOf(value),
            newPos = index - (by || 1);

        if (index === -1)
            throw new Error("Element not found in array");

        if (newPos < 0)
            newPos = 0;

        this.splice(index, 1);
        this.splice(newPos, 0, value);
    };

    Array.prototype.moveDown = function (value, by) {
        var index = this.indexOf(value),
            newPos = index + (by || 1);

        if (index === -1)
            throw new Error("Element not found in array");

        if (newPos >= this.length)
            newPos = this.length;

        this.splice(index, 1);
        this.splice(newPos, 0, value);
    };



    var arr = ['banana', 'curyWurst', 'pc', 'remembaHaruMembaru'];

    alert('withiout changes= '+arr[0]+' ||| '+arr[1]+' ||| '+arr[2]+' ||| '+arr[3]);
    arr.moveDown(arr[2]);


    alert('third word moved down= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
    arr.moveUp(arr[2]);
    alert('third word moved up= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);

http://plnkr.co/edit/JaiAaO7FQcdPGPY6G337?p=preview

Arthur Tsidkilov
fonte
2

Acabei combinando dois deles para trabalhar um pouco melhor ao mover distâncias pequenas e grandes. Eu obtenho resultados bastante consistentes, mas isso provavelmente poderia ser modificado um pouco por alguém mais inteligente do que eu para trabalhar de maneira diferente em tamanhos diferentes etc.

O uso de alguns dos outros métodos ao mover objetos pequenas distâncias foi significativamente mais rápido (x10) do que o uso de emenda. Porém, isso pode variar dependendo dos comprimentos da matriz, mas é verdade para matrizes grandes.

function ArrayMove(array, from, to) {
    if ( Math.abs(from - to) > 60) {
        array.splice(to, 0, array.splice(from, 1)[0]);
    } else {
        // works better when we are not moving things very far
        var target = array[from];
        var inc = (to - from) / Math.abs(to - from);
        var current = from;
        for (; current != to; current += inc) {
            array[current] = array[current + inc];
        }
        array[to] = target;    
    }
}

http://jsperf.com/arraymove-many-sizes

Andrew Backer
fonte
2

Afirma-se em muitos lugares ( adicionar funções personalizadas ao Array.prototype ) jogar com o protótipo Array pode ser uma má idéia, de qualquer maneira eu combinei o melhor de várias postagens, vim com isso usando o Javascript moderno:

    Object.defineProperty(Array.prototype, 'immutableMove', {
        enumerable: false,
        value: function (old_index, new_index) {
            var copy = Object.assign([], this)
            if (new_index >= copy.length) {
                var k = new_index - copy.length;
                while ((k--) + 1) { copy.push(undefined); }
            }
            copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
            return copy
        }
    });

    //how to use it
    myArray=[0, 1, 2, 3, 4];
    myArray=myArray.immutableMove(2, 4);
    console.log(myArray);
    //result: 0, 1, 3, 4, 2

A esperança pode ser útil para qualquer pessoa

BernieSF
fonte
2

Esta versão não é ideal para todos os fins, e nem todo mundo gosta de expressões de vírgula, mas aqui está uma linha que é uma expressão pura, criando uma nova cópia:

const move = (from, to, ...a) => (a.splice(to, 0, ...a.splice(from, 1)), a)

Uma versão com um pouco de desempenho aprimorado retorna a matriz de entrada se nenhuma movimentação for necessária, ainda assim é aceitável para uso imutável, pois a matriz não muda e ainda é uma expressão pura:

const move = (from, to, ...a) => 
    from === to 
    ? a 
    : (a.splice(to, 0, ...a.splice(from, 1)), a)

A invocação de qualquer um é

const shuffled = move(fromIndex, toIndex, ...list)

ou seja, depende da propagação para gerar uma nova cópia. O uso de uma aridade fixa 3 movecolocaria em risco a propriedade de expressão única, a natureza não destrutiva ou o benefício de desempenho splice. Novamente, é mais um exemplo que atende a alguns critérios do que uma sugestão para uso em produção.

Robert Monfera
fonte
1

Array.move.js

Sumário

Move elementos dentro de uma matriz, retornando uma matriz contendo os elementos movidos.

Sintaxe

array.move(index, howMany, toIndex);

Parâmetros

index : Índice no qual mover elementos. Se negativo, o índice começará do final.

howMany : Número de elementos para mover do índice .

toIndex : Índice da matriz na qual colocar os elementos movidos. Se negativo, o toIndex começará do final.

Uso

array = ["a", "b", "c", "d", "e", "f", "g"];

array.move(3, 2, 1); // returns ["d","e"]

array; // returns ["a", "d", "e", "b", "c", "f", "g"]

Polyfill

Array.prototype.move || Object.defineProperty(Array.prototype, "move", {
    value: function (index, howMany, toIndex) {
        var
        array = this,
        index = parseInt(index) || 0,
        index = index < 0 ? array.length + index : index,
        toIndex = parseInt(toIndex) || 0,
        toIndex = toIndex < 0 ? array.length + toIndex : toIndex,
        toIndex = toIndex <= index ? toIndex : toIndex <= index + howMany ? index : toIndex - howMany,
        moved;

        array.splice.apply(array, [toIndex, 0].concat(moved = array.splice(index, howMany)));

        return moved;
    }
});
Jonathan Neal
fonte
2
Embora .movepareça que funcione (não testei), observe que não faz parte de nenhum padrão. Também é bom avisar às pessoas que as funções polyfill / monkeypatched podem quebrar algum código que assume que tudo o que é enumerável é deles.
Jeremy J Starcher
1
a = ["a", "b", "c"]; a.move (0,1,1); // a = [ "a", "b", "c"], deve ser [ "b", "a", "c"]
Leonard Pauli
2
Esse recurso está obsoleto e talvez não seja mais compatível. Tenha cuidado Veja: developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
Mostafa 18/18
1

Usei a boa resposta de @Reid , mas tive dificuldade em mover um elemento do final de uma matriz um passo adiante - para o início (como em um loop ). Por exemplo, ['a', 'b', 'c'] deve se tornar ['c', 'a', 'b'] chamando .move (2,3)

Consegui isso alterando o caso de new_index> = this.length.

Array.prototype.move = function (old_index, new_index) {
        console.log(old_index + " " + new_index);
        while (old_index < 0) {
            old_index += this.length;
        }
        while (new_index < 0) {
            new_index += this.length;
        }
        if (new_index >= this.length) {
            new_index = new_index % this.length;
        }
        this.splice(new_index, 0, this.splice(old_index, 1)[0]);
        return this; // for testing purposes
    };
Marcel Böttcher
fonte
1

Como complemento à excelente resposta de Reid (e porque não posso comentar); Você pode usar o módulo para fazer com que índices negativos e índices muito grandes "rolem":

function array_move(arr, old_index, new_index) {
  new_index =((new_index % arr.length) + arr.length) % arr.length;
  arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
  return arr; // for testing
}

// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1)); 

Didi
fonte
Sim - como os índices negativos são suportados, parece sensato agrupar índices muito grandes em vez de inserir valores indefinidos, na minha opinião.
python1981
1

const move = (from, to, ...a) =>from === to ? a : (a.splice(to, 0, ...a.splice(from, 1)), a);
const moved = move(0, 2, ...['a', 'b', 'c']);
console.log(moved)

Shijo Rs
fonte
1

Eu pensei que este era um problema de troca, mas não é. Aqui está minha solução de uma linha:

const move = (arr, from, to) => arr.map((item, i) => i === to ? arr[from] : (i >= Math.min(from, to) && i <= Math.max(from, to) ? arr[i + Math.sign(to - from)] : item));

Aqui está um pequeno teste:

let test = ['a', 'b', 'c', 'd', 'e'];
console.log(move(test, 0, 2)); // [ 'b', 'c', 'a', 'd', 'e' ]
console.log(move(test, 1, 3)); // [ 'a', 'c', 'd', 'b', 'e' ]
console.log(move(test, 2, 4)); // [ 'a', 'b', 'd', 'e', 'c' ]
console.log(move(test, 2, 0)); // [ 'c', 'a', 'b', 'd', 'e' ]
console.log(move(test, 3, 1)); // [ 'a', 'd', 'b', 'c', 'e' ]
console.log(move(test, 4, 2)); // [ 'a', 'b', 'e', 'c', 'd' ]
console.log(move(test, 4, 0)); // [ 'e', 'a', 'b', 'c', 'd' ]
cagdas_ucar
fonte
Bem, a pergunta não era sobre a troca de itens. O autor solicitou uma solução para uma estratégia de pastilhas.
Andreas Dolk
No que diz respeito à questão em questão, esta é objetivamente a resposta errada.
Ben Steward
0
let ar = ['a', 'b', 'c', 'd'];

function change( old_array, old_index , new_index ){

  return old_array.map(( item , index, array )=>{
    if( index === old_index ) return array[ new_index ];
    else if( index === new_index ) return array[ old_index ];
    else return item;
  });

}

let result = change( ar, 0, 1 );

console.log( result );

resultado:

["b", "a", "c", "d"]
Naycho334
fonte
0

    let oldi, newi, arr;
    
    if(newi !== oldi) {
      let el = this.arr.splice(oldi, 1);
      if(newi > oldi && newi === (this.arr.length + 2)) {
        this.arr.push("");
      }
      this.arr.splice(newi, 0, el);
      if(newi > oldi && newi === (this.arr.length + 2)) {
        this.arr.pop();
      }
    }

behnam
fonte
1
Bem-vindo ao SO! Existem 21 respostas adicionais ... então, por favor, não coloque apenas o código. Explique o benefício da sua resposta.
David García Bodego 27/11/19
0

var ELEMS = ['a', 'b', 'c', 'd', 'e'];
/*
    Source item will remove and it will be placed just after destination
*/
function moveItemTo(sourceItem, destItem, elements) {
    var sourceIndex = elements.indexOf(sourceItem);
    var destIndex = elements.indexOf(destItem);
    if (sourceIndex >= -1 && destIndex > -1) {
        elements.splice(destIndex, 0, elements.splice(sourceIndex, 1)[0]);
    }
    return elements;
}
console.log('Init: ', ELEMS);
var result = moveItemTo('a', 'c', ELEMS);
console.log('BeforeAfter: ', result);

Mohd Abdul Baquee
fonte
0

Versão imutável sem cópia de matriz:

const moveInArray = (arr, fromIndex, toIndex) => {
  if (toIndex === fromIndex || toIndex >= arr.length) return arr;

  const toMove = arr[fromIndex];
  const movedForward = fromIndex < toIndex;

  return arr.reduce((res, next, index) => {
    if (index === fromIndex) return res;
    if (index === toIndex) return res.concat(
      movedForward ? [next, toMove] : [toMove, next]
    );

    return res.concat(next);
  }, []);
};
VoloshinS
fonte
0

Eu acho que a melhor maneira é definir uma nova propriedade para Arrays

Object.defineProperty(Array.prototype, 'move', {
    value: function (old_index, new_index) {
        while (old_index < 0) {
            old_index += this.length;
        }
        while (new_index < 0) {
            new_index += this.length;
        }
        if (new_index >= this.length) {
            let k = new_index - this.length;
            while ((k--) + 1) {
                this.push(undefined);
            }
        }
        this.splice(new_index, 0, this.splice(old_index, 1)[0]);
        return this;
    }
});

console.log([10, 20, 30, 40, 50].move(0, 1));  // [20, 10, 30, 40, 50]
console.log([10, 20, 30, 40, 50].move(0, 2));  // [20, 30, 10, 40, 50]
iProDev
fonte
0

Outra variante JS pura usando o operador de espalhamento de matriz ES6 sem mutação

const reorder = (array, sourceIndex, destinationIndex) => {
	const smallerIndex = Math.min(sourceIndex, destinationIndex);
	const largerIndex = Math.max(sourceIndex, destinationIndex);

	return [
		...array.slice(0, smallerIndex),
		...(sourceIndex < destinationIndex
			? array.slice(smallerIndex + 1, largerIndex + 1)
			: []),
		array[sourceIndex],
		...(sourceIndex > destinationIndex
			? array.slice(smallerIndex, largerIndex)
			: []),
		...array.slice(largerIndex + 1),
	];
}

// returns ['a', 'c', 'd', 'e', 'b', 'f']
console.log(reorder(['a', 'b', 'c', 'd', 'e', 'f'], 1, 4))
      
 

abr
fonte
0

Este método preserva a matriz original e verifica se há erros de delimitação.

const move = (from, to, arr) => {
    to = Math.max(to,0)
    from > to 
        ? [].concat(
            arr.slice(0,to), 
            arr[from], 
            arr.filter((x,i) => i != from).slice(to)) 
        : to > from
            ? [].concat(
                arr.slice(0, from), 
                arr.slice(from + 1, to + 1), 
                arr[from], 
                arr.slice(to + 1))
            : arr}
Nikk Wong
fonte