Spostare un elemento dell'array da una posizione dell'array a un'altra


522

Sto facendo fatica a capire come spostare un elemento array. Ad esempio, dato quanto segue:

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

Come posso scrivere una funzione per spostarmi 'd' prima 'b'?

O 'a' dopo 'c'?

Dopo lo spostamento, gli indici del resto degli elementi dovrebbero essere aggiornati. Ciò significa che nel primo esempio dopo lo spostamento arr [0] avrebbe = 'a', arr [1] = 'd' arr [2] = 'b', arr [3] = 'c', arr [4] = 'e'

Sembra che dovrebbe essere piuttosto semplice, ma non riesco ad avvolgerlo con la testa.


3
bene questa domanda vecchia ma d'oro
Jalal,

usando ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa

Questo scambia gli elementi su inite target.
Matt F.

Risposte:


671

Se desideri una versione su npm, array-move è la più vicina a questa risposta, sebbene non sia la stessa implementazione. Vedi la sua sezione di utilizzo per maggiori dettagli. La versione precedente di questa risposta (che ha modificato Array.prototype.move) è disponibile su npm su array.prototype.move .


Ho avuto un discreto successo con questa funzione:

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

Si noti che l'ultimo returnè solo a scopo di test: spliceesegue operazioni sull'array sul posto, quindi non è necessario un ritorno. Per estensione, si movetratta di un'operazione sul posto. Se si desidera evitare ciò e restituire una copia, utilizzare slice.

Passando attraverso il codice:

  1. Se new_indexè maggiore della lunghezza dell'array, vogliamo (presumo) di riempire correttamente l'array con nuovi undefineds. Questo piccolo frammento gestisce ciò spingendo undefinedl'array fino a quando non abbiamo la lunghezza corretta.
  2. Quindi, dentro arr.splice(old_index, 1)[0], eliminiamo il vecchio elemento. splicerestituisce l'elemento che è stato eliminato, ma è in un array. Nel nostro esempio sopra, questo era [1]. Quindi prendiamo il primo indice di quell'array per ottenere il raw 1lì.
  3. Quindi usiamo spliceper inserire questo elemento al posto di new_index. Poiché abbiamo riempito l'array sopra se new_index > arr.length, probabilmente apparirà nel posto giusto, a meno che non abbiano fatto qualcosa di strano come passare un numero negativo.

Una versione più elaborata per tenere conto degli indici negativi:

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

Che dovrebbe tenere conto di cose come array_move([1, 2, 3], -1, -2)correttamente (sposta l'ultimo elemento dal secondo all'ultimo posto). Il risultato dovrebbe essere quello [1, 3, 2].

Ad ogni modo, nella tua domanda originale, faresti array_move(arr, 0, 2)per adopo c. Per dprima b, lo faresti array_move(arr, 3, 1).


19
Funziona perfettamente! E la tua spiegazione è molto chiara. Grazie per aver dedicato del tempo a scrivere questo.
Mark Brown,

16
Non dovresti manipolare i prototipi di oggetti e array, perché causano problemi durante l'iterazione degli elementi.
burak emre,

9
@burakemre: penso che la conclusione non sia così chiaramente raggiunta. I programmatori JS più bravi (e le librerie più popolari) useranno un .hasOwnPropertycontrollo durante l'iterazione con cose come for..in, in particolare con librerie come Prototype e MooTools che modificano i prototipi. Ad ogni modo, non pensavo che fosse un problema particolarmente importante in un esempio relativamente limitato come questo, e c'è una bella divisione nella comunità sul fatto che la modifica del prototipo sia una buona idea. Normalmente, tuttavia, i problemi di iterazione sono la minima preoccupazione.
Reid

3
Non è necessario per il loop nel passaggio 1, è possibile utilizzare semplicemente this[new_index] = undefined;all'interno del ifblocco. Poiché le matrici Javascript sono sparse, ciò estenderà la dimensione dell'array per includere new_index .spliceaffinché funzioni, ma senza la necessità di creare elementi intermedi .
Michael,

3
@Michael: buon punto - ma il fare this[new_index] = undefinedeffettivamente metterà uno undefinedslot nello array prima dell'indice corretto. (Ad esempio, [1,2,3].move(0,10)avrà 1nello slot 10 e undefinednello slot 9.) Piuttosto, se la scarsità è OK, potremmo fare a this[new_index] = this.splice(old_index, 1)[0]meno dell'altra chiamata di splicing (rendendola invece if / else).
Reid,

268

Ecco un esempio che ho trovato su JSPerf ....

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

che è fantastico da leggere, ma se vuoi prestazioni (in piccoli set di dati) prova ...

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

Non posso prendermi il merito, dovrebbe andare tutto a Richard Scarrott . Supera il metodo basato sulla giunzione per insiemi di dati più piccoli in questo test delle prestazioni . Tuttavia, è significativamente più lento su set di dati più grandi, come sottolinea Darwayne .


2
La tua soluzione più performante è più lenta su set di dati di grandi dimensioni. jsperf.com/array-prototype-move/8
Darwayne

44
Sembra un commercio davvero sciocco. Le prestazioni su set di dati di piccole dimensioni sono un guadagno trascurabile, ma la perdita su set di dati di grandi dimensioni è una perdita significativa. Lo scambio netto è negativo.
Kyeotic,

3
@Reid Non era un requisito. IMO, va bene supporre che la lunghezza dell'array non venga modificata.
robsch

3
Una soluzione di una linea deve gestire due situazioni:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L

13
Non modificare mai i prototipi integrati, mai. nczonline.net/blog/2010/03/02/…
LJHarb

231

Mi piace così È conciso e funziona.

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

Nota: ricordarsi sempre di controllare i limiti dell'array.

Esegui Snippet su jsFiddle


29
Poiché Array.splice restituisce il / i valore / i rimosso / i in una nuova matrice, è possibile scriverlo come una riga ... arr.splice (indice + 1, 0, arr.splice (indice, 1) [0]);
Eric

49
Personalmente preferisco il codice a 3 righe. È più facile da capire: ottieni una copia dell'elemento; rimuoverlo dall'array; inserirlo in una nuova posizione. L'unico liner è più corto ma non così chiaro da capire per gli altri ...
Philipp

2
Codice breve e semplice. Ma è il 2019 !!, Creare un clone dell'array e restituirlo invece di mutare l'array. Ciò renderà la tua funzione "arraymove" conforme agli standard di programmazione funzionale
SamwellTarly

5
OK ma non tutto né èdeve essere conforme alla programmazione funzionale; inoltre questo può ancora essere utile nella programmazione funzionale all'interno di una procedura per manipolare array locali.
SteakOverflow

36

Il metodo splice () aggiunge / rimuove gli elementi in / da un array e restituisce gli elementi rimossi .

Nota: questo metodo modifica l'array originale. / 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"]

poiché la funzione è concatenabile funziona anche questo:

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

demo qui


C'è qualche biblioteca che lo utilizza? Piuttosto pulito!
Uicoded

Vedi altri commenti a riguardo: è una cattiva idea modificare prototipi integrati come Array e Object. Spezzerai le cose.
geoidesic,

27

Il mio 2c. Facile da leggere, funziona, è veloce, non crea nuove matrici.

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

2
Alla prima stringa di funzione dovresti tornare array, come ha fatto alla fine.
Sergey Voronezhskiy,

3
Vero come mi sono perso? Fisso!
Merc

Mi piace di più la tua soluzione semplice e flessibile. Grazie!
Roman M. Koss,

18

Ho avuto questa idea da @Reid di spingere qualcosa al posto dell'elemento che dovrebbe essere spostato per mantenere costante la dimensione dell'array. Ciò semplifica i calcoli. Inoltre, la spinta di un oggetto vuoto ha l'ulteriore vantaggio di poterlo cercare in modo univoco in un secondo momento. Questo funziona perché due oggetti non sono uguali finché non si riferiscono allo stesso oggetto.

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

Ecco quindi la funzione che accetta l'array di origine e l'indice di destinazione. È possibile aggiungerlo ad Array.prototype se necessario.

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

1
Sembra promettente ... e non lo sapevo sui confronti di javascript js. Grazie!
Mark Brown,

Non funziona per il caso sourceIndex = 0,destIndex = 1
Sergey Voronezhskiy,

destIndexè pensato per essere l'indice prima che l'elemento source venga spostato nell'array.
Anurag,

Questa è la risposta migliore finora. Altre risposte hanno fallito un paio di test unitari nella mia suite (spostare l'oggetto in avanti)
Ilya Ivanov

16

Questo si basa sulla soluzione di @ Reid. ad eccezione di:

  • Non sto cambiando il Arrayprototipo.
  • Lo spostamento di un oggetto fuori limite a destra non crea undefinedelementi, ma sposta l'elemento nella posizione più a destra.

Funzione:

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

Test unitari:

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

questo è sbagliato, se inserisci una posizione di post, l'indice cambierà poiché hai rimosso l'elemento
Yao Zhao

Grazie. Volevo rimuovere un elemento da un array senza lasciare un elemento null (che si è verificato durante l'utilizzo di splice (indexToRemove). Ho usato il tuo metodo per spostare l'elemento che volevo rimuovere alla fine dell'array, quindi ho usato pop () metodo da rimuovere
Luke Schoen,

mi è piaciuto "spostare l'elemento nella posizione più a destra", utile per il mio caso. grazie
bFun

11

Ecco la mia soluzione ES6 a una fodera con un parametro opzionale on.

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

Adattamento della prima soluzione proposta da digiguru

Il parametro onè il numero di elementi che iniziano da fromspostare.


La soluzione va bene Tuttavia, quando si espande un prototipo non è necessario utilizzare la funzione freccia perché in questo caso 'this' non è un'istanza di array ma, ad esempio, un oggetto Window.
wawka,

7

Il splicemetodo Arraypotrebbe essere utile: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Tieni presente che potrebbe essere relativamente costoso poiché deve reindicizzare attivamente l'array.


Sì, ma non appena eseguo la giunzione, gli indici dell'array vengono aggiornati, il che mi rende difficile capire dove posizionare l'elemento che ho appena rimosso. Soprattutto perché ho bisogno della funzione per essere in grado di gestire i movimenti in entrambe le direzioni.
Mark Brown,

@Mark: non collegare la stringa e salvarla nella stessa variabile, creare una nuova stringa e collegarla. Vedi la mia risposta qui sotto.
Jared Updike,

7

Un approccio sarebbe quello di creare un nuovo array con i pezzi nell'ordine desiderato, usando il metodo slice.

Esempio

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) ti dà ['a']
  • arr.slice (2,4) ti dà ['b', 'c']
  • arr.slice (4) ti dà ['e']

1
Ti rendi conto che arr2finisci per essere una stringa a causa delle operazioni di concatenazione, giusto? :) Finisce per essere "adc,de".
Ken Franqueiro,

6

È possibile implementare alcuni calcoli di base e creare una funzione universale per spostare l'elemento dell'array da una posizione all'altra.

Per JavaScript è simile al seguente:

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; 

}

Dai un'occhiata a "elementi array mobili" su "gloommatter" per una spiegazione dettagliata.

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


1
Questa dovrebbe essere la risposta corretta, in quanto non alloca alcun nuovo array. Grazie!
Cᴏʀʏ

Il collegamento è interrotto.
Rokit

6

Ho implementato una ECMAScript 6soluzione immutabile basata sulla @Mercrisposta qui:

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

I nomi delle variabili possono essere abbreviati, basta usare quelli lunghi in modo che il codice possa spiegarsi.


sicuramente una risposta migliore, le mutazioni creano effetti collaterali
Matt Lo

1
Per curiosità, perché non semplicemente tornare arrayimmediatamente se fromIndex === toIndexe creare solo newArrayse non è così? Immutabilità non significa che una nuova copia deve essere creata per chiamata di funzione anche quando non ci sono cambiamenti. Basta chiedere a b / c il motivo per la maggiore lunghezza di questa funzione (rispetto alle linee di giunzione basate su giunzioni) è prestazione e fromIndexpuò benissimo essere uguale toIndex, a seconda dell'uso.
Robert Monfera,

5

Avevo bisogno di un metodo di spostamento immutabile (uno che non modificasse l'array originale), quindi ho adattato la risposta accettata di @ Reid per usare semplicemente Object.assign per creare una copia dell'array prima di eseguire la giunzione.

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

Ecco un jsfiddle che lo mostra in azione .


È sempre bello vedere ppl prendere in considerazione le mutazioni.
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


2

Ho finito per combinare due di questi per lavorare un po 'meglio quando si spostano sia le piccole che le grandi distanze. Ottengo risultati abbastanza coerenti, ma probabilmente questo potrebbe essere modificato un po 'da qualcuno più intelligente di me per lavorare in modo diverso per dimensioni diverse, ecc.

L'uso di alcuni degli altri metodi per spostare oggetti su piccole distanze era significativamente più veloce (x10) rispetto all'utilizzo della giunzione. Questo potrebbe cambiare a seconda della lunghezza dell'array, ma è vero per array di grandi dimensioni.

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


2

È affermato in molti punti ( aggiungendo funzioni personalizzate in Array.prototype ) giocare con il prototipo Array potrebbe essere una cattiva idea, comunque ho combinato il meglio da vari post, sono arrivato con questo, usando 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

La speranza può essere utile a chiunque


2

Questa versione non è l'ideale per tutti gli scopi, e non a tutti piacciono le espressioni virgola, ma ecco una riga che è un'espressione pura, che crea una nuova copia:

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

Una versione leggermente migliorata delle prestazioni restituisce l'array di input se non è necessaria alcuna mossa, è ancora OK per un uso immutabile, poiché l'array non cambierà ed è ancora una pura espressione:

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

L'invocazione di entrambi è

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

cioè si basa sulla diffusione per generare una nuova copia. L'uso di un'arità fissa 3 movecomprometterebbe la proprietà della singola espressione o la natura non distruttiva o il vantaggio in termini di prestazioni splice. Ancora una volta, è più un esempio che soddisfa alcuni criteri che un suggerimento per l'uso in produzione.


1

Array.move.js

Sommario

Sposta gli elementi all'interno di un array, restituendo un array contenente gli elementi spostati.

Sintassi

array.move(index, howMany, toIndex);

parametri

indice : indice in cui spostare gli elementi. Se negativo, l' indice inizierà dalla fine.

howMany : numero di elementi da spostare dall'indice .

toIndex : indice dell'array in cui posizionare gli elementi spostati. Se negativo, toIndex inizierà dalla fine.

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

2
Mentre .movesembra che dovrebbe funzionare (non l'ho testato), dovresti notare che non fa parte di alcuno standard. È anche bene avvertire la gente che le funzioni polyfill / monkeypatched possono violare un po 'di codice che presume che tutto ciò che è elencabile sia loro.
Jeremy J Starcher,

1
a = ["a", "b", "c"]; a.move (0,1,1); // a = ["a", "b", "c"], dovrebbe essere ["b", "a", "c"]
Leonard Pauli,

2
Questa funzione è obsoleta e potrebbe non essere più supportata. Fai attenzione Vedi: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mostafa

1

Ho usato la bella risposta di @Reid , ma ho lottato per spostare un elemento dalla fine di un array un passo avanti - all'inizio (come in un ciclo ). Ad esempio ['a', 'b', 'c'] dovrebbe diventare ['c', 'a', 'b'] chiamando .move (2,3)

Ho raggiunto questo obiettivo cambiando il caso per 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
    };

1

In aggiunta all'ottima risposta di Reid (e perché non posso commentare); Puoi usare il modulo per far "roll over" sia gli indici negativi che quelli troppo grandi:

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


Sì, poiché gli indici negativi sono supportati, a mio avviso sembra ragionevole racchiudere indici troppo grandi anziché inserire valori indefiniti.
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)


1

Ho pensato che questo fosse un problema di scambio ma non lo è. Ecco la mia soluzione one-liner:

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

Ecco un piccolo test:

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

Bene, la domanda non riguardava lo scambio di oggetti. L'autore ha chiesto una soluzione per una strategia di inserimento.
Andreas Dolk,

Per quanto riguarda la domanda in esame, questa è oggettivamente la risposta sbagliata.
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 );

risultato:

["b", "a", "c", "d"]

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


1
Benvenuti in SO! Ci sono 21 risposte aggiuntive ... quindi, per favore, non inserire solo il codice. Spiega il vantaggio della tua risposta.
David García Bodego,

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


0

Versione immutabile senza copia dell'array:

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);
  }, []);
};

0

Penso che il modo migliore sia definire una nuova proprietà per gli array

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]

0

Un'altra variante JS pura che utilizza l'operatore di array array ES6 senza mutazione

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


0

Questo metodo preserverà l'array originale e verificherà la presenza di errori di delimitazione.

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}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.