web-dev-qa-db-fra.com

Déplacer un élément de tableau d'une position de tableau à une autre

J'ai du mal à comprendre comment déplacer un élément de tableau. Par exemple, étant donné ce qui suit:

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

Comment écrire une fonction pour déplacer 'd' avant 'b'?

Ou 'a' après 'c'?

Après le déménagement, les indices du reste des éléments doivent être mis à jour. Cela signifie que dans le premier exemple après le déplacement, arr [0] serait = 'a', arr [1] = 'd' arr [2] = 'b', arr [3] = 'c', arr [4] = 'e'

Cela semble être assez simple, mais je ne peux pas comprendre.

437
Mark Brown

Si vous souhaitez une version sur npm, array-move est le plus proche de cette réponse, bien que ce ne soit pas la même implémentation. Voir sa section utilisation pour plus de détails. La version précédente de cette réponse (celle modifiée Array.prototype.move) est disponible sur npm à l'adresse array.prototype.move .


J'ai eu assez de succès avec cette fonction:

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

Notez que la dernière return sert simplement à des fins de test: splice effectue des opérations sur le tableau en place, un retour n'est donc pas nécessaire. Par extension, ceci move est une opération sur place. Si vous voulez éviter cela et renvoyer une copie, utilisez slice .

Parcourir le code:

  1. Si new_index est supérieur à la longueur du tableau, nous voulons (je présume) remplir correctement le tableau avec le nouveau undefineds. Ce petit extrait gère cela en poussant undefined sur le tableau jusqu'à ce que nous ayons la bonne longueur.
  2. Ensuite, dans arr.splice(old_index, 1)[0], nous séparons l'ancien élément. splice renvoie l'élément qui a été épissé, mais il se trouve dans un tableau. Dans notre exemple ci-dessus, il s'agissait de [1]. Nous prenons donc le premier index de ce tableau pour obtenir le 1 brut ici.
  3. Ensuite, nous utilisons splice pour insérer cet élément à la place de new_index. Puisque nous avons rempli le tableau ci-dessus si new_index > arr.length, il apparaîtra probablement au bon endroit, à moins qu’ils n’aient fait quelque chose d’étrange comme passer à un nombre négatif.

Une version plus sophistiquée pour prendre en compte les indices négatifs:

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

Ce qui devrait rendre compte de choses comme array_move([1, 2, 3], -1, -2) correctement (déplacez le dernier élément à l'avant-dernière place). Le résultat pour cela devrait être [1, 3, 2].

Dans les deux cas, dans votre question initiale, vous utiliseriez array_move(arr, 0, 2) pour a après c. Pour d avant b, vous utiliseriez array_move(arr, 3, 1).

611
Reid

Voici un one liner que j'ai trouvé sur JSPerf ....

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

ce qui est génial à lire, mais si vous voulez des performances (dans de petits ensembles de données), essayez ...

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

Je ne peux prendre aucun crédit, tout devrait aller à Richard Scarrott . Il bat la méthode basée sur l'épissure pour les ensembles de données plus petits dans ce test de performance . Il est toutefois nettement plus lent pour les grands ensembles de données comme le souligne Darwayne .

243
digiguru

J'aime cette façon. Cela fonctionne, c'est concis et élégant.

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

Remarque: souvenez-vous toujours de vérifier les limites de votre tableau.

Voici un jsFiddle à tester: https://jsfiddle.net/aq9Laaew/286055/

166
SteakOverflow

La méthode splice () ajoute/supprime des éléments dans/d'un tableau et retourne le élément supprimé .

Remarque: cette méthode modifie le tableau d'origine./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"]

comme la fonction est chainable cela fonctionne aussi:

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

démo ici

32
user669677

Mon 2c. Facile à lire, cela fonctionne, c'est rapide, cela ne crée pas de nouveaux tableaux.

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

@Reid a eu l'idée de placer quelque chose à la place de l'élément qui est supposé être déplacé pour maintenir la taille du tableau constante. Cela simplifie les calculs. En outre, le fait de pousser un objet vide présente l’avantage supplémentaire de pouvoir le rechercher uniquement plus tard. Cela fonctionne parce que deux objets ne sont pas égaux tant qu'ils ne font pas référence au même objet.

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

Alors, voici la fonction qui prend dans le tableau source et les index source et de destination. Vous pouvez l'ajouter à Array.prototype si nécessaire.

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

Ceci est basé sur la solution de @ Reid. Sauf:

  • Je ne change pas le prototype Array.
  • Déplacer un élément hors des limites vers la droite ne crée pas les éléments undefined, il déplace simplement l'élément à la position la plus à droite.

Une fonction:

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

Tests unitaires:

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);
    });
});
15
André Pena

Voici ma solution à un revêtement ES6 avec un paramètre facultatif on.

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

Adaptation de la première solution proposée par digiguru

Le paramètre on est le nombre d'éléments commençant par from que vous souhaitez déplacer.

9
Elie Teyssedou

Une approche serait de créer un nouveau tableau avec les morceaux dans l'ordre de votre choix, en utilisant la méthode slice.

Exemple

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) vous donne ['a']
  • arr.slice (2,4) vous donne ['b', 'c']
  • arr.slice (4) vous donne ['e']
7
Jared Updike

La méthode splice de Array pourrait aider: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Gardez simplement à l'esprit que cela peut être relativement coûteux car il doit réindexer activement le tableau.

6
Ken Franqueiro

Vous pouvez implémenter un calcul de base et créer une fonction universelle pour déplacer un élément de tableau d'une position à l'autre.

Pour JavaScript, cela ressemble à ceci:

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; 

}

Découvrez "éléments de tableau en mouvement" à "gloommatter" pour une explication détaillée.

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

6
Andrea

J'avais besoin d'une méthode de déplacement immuable (une méthode qui ne modifiait pas le tableau d'origine). J'ai donc adapté la réponse acceptée de @ Reid pour simplement utiliser Object.assign pour créer une copie du tableau avant de faire le raccordement.

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

Voici un jsfiddle le montrant en action .

5
Javid Jamae
    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

4
Arthur

J'ai implémenté une solution immuable de ECMAScript 6 basée sur la réponse de @Merc ici:

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

Les noms de variables peuvent être raccourcis, utilisez simplement des noms longs pour que le code puisse s’expliquer.

3
Barry Michael Doyle

Array.move.js

Sommaire

Déplace des éléments dans un tableau, renvoyant un tableau contenant les éléments déplacés.

Syntaxe

array.move(index, howMany, toIndex);

Paramètres

index: Index auquel déplacer des éléments. Si négatif, index commencera à la fin.

combien de fois: Nombre d'éléments à partir duquel index.

toIndex: Index du tableau sur lequel placer les éléments déplacés. Si négatif, toIndex commencera à la fin.

Usage

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
Jonathan Neal

J'ai fini par combiner deux de ces solutions pour travailler un peu mieux lorsque je me déplaçais à la fois sur de petites et de grandes distances. J'obtiens des résultats assez cohérents, mais cela pourrait probablement être légèrement modifié par une personne plus intelligente que moi pour travailler différemment pour des tailles différentes, etc.

L'utilisation de certaines des autres méthodes pour déplacer des objets sur de petites distances était nettement plus rapide (x10) que l'utilisation de l'épissure. Cela peut changer en fonction de la longueur du tableau, mais cela est vrai pour les grands tableaux.

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
Andrew Backer

Cette version n'est pas idéale pour tout le monde, et tout le monde n'aime pas les expressions avec des virgules, mais voici un one-liner qui est une expression pure, créant une nouvelle copie:

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

Une version légèrement plus performante renvoie le tableau en entrée si aucun déplacement n'est nécessaire, il reste correct pour un usage immuable, car le tableau ne change pas et reste une expression pure:

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

L'invocation de l'un ou l'autre est

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

c'est-à-dire qu'il repose sur la propagation pour générer une nouvelle copie. L'utilisation d'une arité fixe 3 move mettrait en péril la propriété d'expression unique, la nature non destructive ou les avantages en termes de performances de splice. Encore une fois, il s’agit plus d’un exemple répondant à certains critères qu’une suggestion d’utilisation en production.

2
Robert Monfera

J'ai utilisé le Nice answer of @Reid , mais j'ai eu du mal à déplacer un élément de la fin d'un tableau un peu plus loin - jusqu'au début (comme dans une boucle ). Par exemple. ['a', 'b', 'c'] devrait devenir ['c', 'a', 'b'] en appelant .move (2,3)

J'ai atteint cet objectif en modifiant la casse 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
    };
1
Marcel Böttcher

En plus de excellente réponse de Reid (et parce que je ne peux pas commenter); Vous pouvez utiliser modulo pour "survoler" les index négatifs et les index trop grands:

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

Il est indiqué dans de nombreux endroits ( ajout de fonctions personnalisées dans Array.prototype ) jouer avec le prototype Array pourrait être une mauvaise idée. Quoi qu'il en soit, j'ai combiné le meilleur de plusieurs articles. :

    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

L'espoir peut être utile à n'importe qui

1
BernieSF
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)
0
Shijo Rs
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 );

résultat:

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