608 votes

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

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

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

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

Ou 'a' après 'c' ?

Après le déplacement, les indices du reste des éléments doivent être mis à jour. Cela signifie que dans le premier exemple, après le déplacement,

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

Cela semble assez simple, mais je n'arrive pas à m'y retrouver.

0 votes

En utilisant ES6 const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}

2 votes

Il suffit de permuter les éléments à init y target .

758voto

Reid Points 4229

Si vous souhaitez une version sur npm, array-move est ce qui se rapproche le plus de cette réponse, bien qu'il ne s'agisse pas de la même implémentation. Voir sa section utilisation pour plus de détails. La version précédente de cette réponse (qui modifiait Array.prototype.move) peut être trouvée sur npm à l'adresse suivante array.prototype.move .


J'ai eu un assez bon 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 le dernier return est simplement à des fins de test : splice effectue les opérations sur le tableau en place, donc un retour n'est pas nécessaire. Par extension, cette move est une opération "in-place". Si vous voulez éviter cela et retourner une copie, utilisez slice .

Passer à travers le code :

  1. Si new_index est supérieure à la longueur du tableau, nous voulons (je suppose) remplir correctement le tableau avec de nouvelles données de type undefined s. Ce petit bout de code gère cela en poussant undefined sur le tableau jusqu'à ce que nous ayons la bonne longueur.
  2. Ensuite, en arr.splice(old_index, 1)[0] on retire l'ancien élément. splice renvoie l'élément qui a été découpé, mais c'est dans un tableau. Dans notre exemple ci-dessus, c'était [1] . Donc, nous prenons le premier indice de ce tableau pour obtenir l'indice brut. 1 là.
  3. Ensuite, nous utilisons splice pour insérer cet élément à la place du 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 entrer un nombre négatif.

Une version plus sophistiquée pour tenir compte des 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 tenir compte de choses comme array_move([1, 2, 3], -1, -2) correctement (déplacer le dernier élément à l'avant-dernière place). Le résultat devrait être [1, 3, 2] .

Quoi qu'il en soit, dans votre question originale, vous feriez array_move(arr, 0, 2) para a après c . Pour d avant b vous feriez array_move(arr, 3, 1) .

22 votes

Cela fonctionne parfaitement ! Et votre explication est très claire. Merci d'avoir pris le temps de rédiger ce document.

17 votes

Vous ne devriez pas manipuler les prototypes Object et Array, cela pose des problèmes lors de l'itération des éléments.

10 votes

@burakemre : Je pense que cette conclusion n'est pas si claire. La plupart des bons programmeurs JS (et la plupart des bibliothèques populaires) utiliseront une fonction .hasOwnProperty vérifier lors de l'itération avec des choses comme for..in, surtout avec des bibliothèques comme Prototype et MooTools qui modifient les prototypes. Quoi qu'il en soit, je ne pensais pas que c'était un problème particulièrement important dans un exemple relativement limité comme celui-ci, et il y a une belle division dans la communauté sur la question de savoir si la modification des prototypes est une bonne idée ou non. Normalement, les problèmes d'itération sont les moins préoccupants.

317voto

SteakOverflow Points 220

J'aime cette façon de faire. C'est concis et ça marche.

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

Remarque : n'oubliez jamais de vérifier les limites de votre tableau.

Exécuter le snippet dans jsFiddle

35 votes

Puisque Array.splice renvoie la ou les valeurs supprimées dans un nouveau tableau, vous pouvez l'écrire en une seule ligne... arr.splice(index + 1, 0, arr.splice(index, 1)[0]) ;

71 votes

Personnellement, je préfère le code à 3 lignes. C'est plus facile à comprendre : Obtenir une copie de l'élément ; le retirer du tableau ; l'insérer à une nouvelle position. Le code en une ligne est plus court mais pas aussi clair pour les autres...

5 votes

Un code court et simple. Mais on est en 2019 !!, Créez un clone du tableau et renvoyez-le au lieu de muter le tableau. Cela rendra votre fonction "arraymove" conforme aux normes de la programmation fonctionnelle.

309voto

digiguru Points 3305

Voici une phrase que j'ai trouvée sur JSPerf.....

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

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 pas m'attribuer le mérite, tout doit aller à Richard Scarrott . Elle bat la méthode basée sur l'épissure pour les ensembles de données plus petits dans ce domaine. test de performance . Il est toutefois nettement plus lent sur les grands ensembles de données. comme le souligne Darwayne .

0 votes

La méthode move2 est intéressante, car elle permet de déplacer un élément du tableau vers le haut ou vers le bas du tableau. J'ai rencontré des problèmes avec certaines des autres solutions lorsque je déplaçais des éléments du tableau vers la droite.

2 votes

Votre solution la plus performante est plus lente sur les grands ensembles de données. jsperf.com/array-prototype-move/8

51 votes

Cela semble être un compromis vraiment stupide. La performance sur les petits ensembles de données est un gain négligeable, mais la perte sur les grands ensembles de données est une perte significative. Votre échange net est négatif.

40voto

2astalavista Points 7092

La méthode splice() ajoute/supprime des éléments à/depuis un tableau, et renvoie la méthode supprimé article(s).

Note : Cette méthode modifie le tableau 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"]

car la fonction est enchaînable cela fonctionne aussi :

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

Démonstration ici

0 votes

Y a-t-il une bibliothèque qui utilise ceci ? C'est très intéressant !

3 votes

Voir les autres commentaires à ce sujet : c'est une mauvaise idée de modifier les prototypes intégrés comme Array et Object. Vous allez casser des choses.

20voto

Anurag Points 66470

J'ai eu l'idée de @Reid de pousser quelque chose à la place de l'élément qui est censé être déplacé pour garder 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 de manière unique par la suite. Cela fonctionne parce que deux objets ne sont pas égaux tant qu'ils ne font pas référence au même objet.

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

Voici donc la fonction qui prend en compte le tableau source, et les index source et 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);
}

1 votes

Cela semble prometteur... et je ne connaissais pas les comparaisons javascript js. Merci !

0 votes

Ne fonctionne pas pour le cas sourceIndex = 0 , destIndex = 1

0 votes

destIndex est censé être l'index avant que l'élément source ne soit déplacé dans le tableau.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X