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

522

J'ai du mal à trouver comment déplacer un élément de tableau. Par exemple, compte tenu des éléments suivants:

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

Comment puis-je écrire une fonction à déplacer 'd'auparavant '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 [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 envelopper ma tête.

Mark Brown
la source
3
bien cette question ancienne mais dorée
Jalal
en utilisant ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa
Cela permute simplement les éléments à initet target.
Matt F.

Réponses:

671

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 d'utilisation pour plus de détails. La version précédente de cette réponse (qui a modifié Array.prototype.move) peut être trouvée sur npm à 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 returnest simplement à des fins de test: spliceeffectue des opérations sur le tableau sur place, donc un retour n'est pas nécessaire. Par extension, il moves'agit d'une opération sur place. Si vous voulez éviter cela et renvoyer une copie, utilisez slice.

Parcourir le code:

  1. Si new_indexest supérieur à la longueur du tableau, nous voulons (je présume) remplir correctement le tableau avec de nouveaux undefineds. Ce petit extrait gère cela en poussant undefinedsur le tableau jusqu'à ce que nous ayons la bonne longueur.
  2. Puis, à l' arr.splice(old_index, 1)[0]intérieur, nous épissons l'ancien élément. splicerenvoie l'élément qui a été épissé, mais il est dans un tableau. Dans notre exemple ci-dessus, c'était [1]. Nous prenons donc le premier index de ce tableau pour y obtenir le brut 1.
  3. Ensuite, nous utilisons splicepour insérer cet élément à la place de new_index. Puisque nous avons complété le tableau ci-dessus new_index > arr.length, il apparaîtra probablement au bon endroit, à moins qu'ils n'aient fait quelque chose d'étrange comme passer dans 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 expliquer des choses comme array_move([1, 2, 3], -1, -2)correctement (déplacer le dernier élément à l'avant-dernier endroit). Le résultat devrait être [1, 3, 2].

De toute façon, dans votre question d'origine, vous feriez array_move(arr, 0, 2)pour aaprès c. Pour davant b, vous le feriez array_move(arr, 3, 1).

Reid
la source
19
Cela fonctionne parfaitement! Et votre explication est très claire. Merci d'avoir pris le temps d'écrire ceci.
Mark Brown
16
Vous ne devez pas manipuler les prototypes d'objets et de tableaux, cela pose des problèmes lors de l'itération des éléments.
burak emre
9
@burakemre: Je pense que cette conclusion n'est pas si clairement atteinte. La plupart des bons programmeurs JS (et les bibliothèques les plus populaires) utiliseront une .hasOwnPropertyvérification lors de l'itération avec des choses comme for..in, en particulier 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 du prototype est une bonne idée. Normalement, les problèmes d'itération sont cependant les moins préoccupants.
Reid
3
Il n'y a pas besoin de la boucle à l'étape 1, vous pouvez simplement l'utiliser this[new_index] = undefined;dans le ifbloc. Comme les tableaux Javascript sont rares, cela étendra la taille du tableau pour inclure le new_index pour que le .splicetravail fonctionne, mais sans avoir besoin de créer des éléments intermédiaires.
Michael
3
@Michael: Bon point - mais faire this[new_index] = undefinedmettra en fait un undefineddans le slot du tableau avant l'index correct. (Par exemple, il [1,2,3].move(0,10)y aura 1dans l'emplacement 10 et undefineddans l'emplacement 9.) Au contraire, si la parcimonie est correcte, nous pourrions nous this[new_index] = this.splice(old_index, 1)[0]passer de l'autre appel d'épissage (en faire un if / else à la place).
Reid
268

Voici un 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 pas prendre de crédit, tout devrait revenir à Richard Scarrott . Il bat la méthode basée sur l'épissure pour les petits ensembles de données dans ce test de performance . Il est cependant beaucoup plus lent sur des ensembles de données plus volumineux, comme le souligne Darwayne .

digiguru
la source
2
Votre solution plus performante est plus lente sur les grands ensembles de données. jsperf.com/array-prototype-move/8
Darwayne
44
Cela semble être un commerce vraiment stupide. Les performances sur les petits ensembles de données sont un gain négligeable, mais la perte sur les grands ensembles de données est une perte importante. Votre échange net est négatif.
Kyeotic
3
@Reid Ce n'était pas une exigence. OMI, il est normal de supposer que la longueur du tableau n'est pas modifiée.
robsch
3
Une solution en ligne doit gérer deux situations:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L
13
Veuillez ne jamais modifier les prototypes intégrés, jamais. nczonline.net/blog/2010/03/02/…
LJHarb
231

J'aime cette façon. 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 pas de vérifier les limites de votre tableau.

Exécutez l'extrait de code sur jsFiddle

SteakOverflow
la source
29
Puisque Array.splice renvoie la ou les valeurs supprimées dans un nouveau Array, vous pouvez l'écrire sous la forme d'une ligne ... arr.splice (index + 1, 0, arr.splice (index, 1) [0]);
Eric
49
Personnellement, je préfère le code à 3 lignes. C'est plus facile à comprendre: obtenez une copie de l'élément; supprimez-le du tableau; insérez-le dans une nouvelle position. La doublure est plus courte mais pas si claire pour que les autres comprennent ...
Philipp
2
Code court et simple. Mais c'est 2019 !!, créez un clone du tableau et retournez-le au lieu de muter le tableau. Cela rendra votre fonction "arraymove" conforme aux normes de programmation fonctionnelle
SamwellTarly
36

La méthode splice () ajoute / supprime des éléments vers / depuis un tableau et renvoie les éléments supprimés .

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 chaînable, cela fonctionne aussi:

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

démo ici


la source
Y a-t-il une bibliothèque qui l'utilise? Génial!
uicodé
Voir d'autres commentaires à ce sujet: c'est une mauvaise idée de modifier des prototypes intégrés comme Array et Object. Vous allez casser des choses.
géoidesic
27

Mon 2c. Facile à lire, il fonctionne, il est rapide, il 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;
}
Merc
la source
2
À la première chaîne de fonction, vous devez retourner array, comme cela a été fait à la fin.
Sergey Voronezhskiy
3
Vrai comment ai-je raté ça? Fixé!
Merc
J'aime le plus votre solution simple et flexible. THX!
Roman M. Koss
18

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. De plus, pousser un objet vide a l'avantage supplémentaire de pouvoir le rechercher uniquement plus tard. Cela fonctionne car deux objets ne sont pas égaux jusqu'à ce qu'ils se réfèrent au même objet.

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

Voici donc la fonction qui prend dans le tableau source, et la source, les index de destination. Vous pouvez l'ajouter au 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);
}
Anurag
la source
1
Cela semble prometteur ... et je ne savais pas que sur les comparaisons javascript js. Merci!
Mark Brown
Ne fonctionne pas pour le cas sourceIndex = 0,destIndex = 1
Sergey Voronezhskiy
destIndexest censé être l'index avant que l'élément source ne soit déplacé dans le tableau.
Anurag
C'est la meilleure réponse jusqu'à présent. D'autres réponses ont échoué quelques tests unitaires dans ma suite (déplacer l'objet vers l'avant)
Ilya Ivanov
16

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

  • Je ne change pas le Arrayprototype.
  • Déplacer un élément hors des limites vers la droite ne crée pas d' undefinedéléments, il déplace simplement l'élément vers 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);
    });
});
André Pena
la source
c'est faux, si vous insérez une position de poste, l'index changera puisque vous avez supprimé l'élément
Yao Zhao
Je vous remercie. Je voulais supprimer un élément d'un tableau sans laisser d'élément nul (ce qui s'est produit lors de l'utilisation de splice (indexToRemove). J'ai utilisé votre méthode pour déplacer l'élément que je voulais supprimer à la fin du tableau, puis j'ai utilisé le pop () méthode à supprimer
Luke Schoen
aimé "déplacer l'élément vers la position la plus à droite", utile pour mon cas. thx
bFunc
11

Voici ma solution one liner ES6 avec un paramètre optionnel 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 onest le nombre d'élément à partir duquel fromvous souhaitez vous déplacer.

Elie Teyssedou
la source
La solution est bonne. Cependant, lorsque vous développez un prototype, vous ne devez pas utiliser la fonction flèche car dans ce cas, «ceci» n'est pas une instance de tableau mais par exemple un objet Window.
wawka
7

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

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

Ken Franqueiro
la source
Oui, mais dès que j'effectue l'épissure, les indices du tableau sont mis à jour, ce qui rend difficile pour moi de comprendre où placer l'élément que je viens de supprimer. D'autant plus que j'ai besoin de la fonction pour pouvoir gérer les mouvements dans les deux sens.
Mark Brown
@Mark: ne pas épisser la chaîne et l'enregistrer dans la même variable, créer une nouvelle chaîne et épisser cela. Voir ma réponse ci-dessous.
Jared Updike
7

Une approche serait de créer un nouveau tableau avec les pièces dans l'ordre que vous souhaitez, en utilisant la méthode de tranche.

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']
Jared Updike
la source
1
Vous vous rendez compte que vous arr2finissez par être une chaîne en raison des opérations de concaténation, non? :) Cela finit par être "adc,de".
Ken Franqueiro
6

Vous pouvez implémenter un calcul de base et créer une fonction universelle pour déplacer l'é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; 

}

Consultez les "éléments du tableau en mouvement" sur "gloommatter" pour une explication détaillée.

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

Andrea
la source
1
Cela devrait être la bonne réponse, car il n'alloue aucun nouveau tableau. Merci!
Cᴏʀʏ
Le lien est rompu.
Rokit
6

J'ai implémenté une ECMAScript 6solution immuable basée sur @Mercla réponse de 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, il suffit d'utiliser des noms longs pour que le code puisse s'expliquer.

Barry Michael Doyle
la source
certainement une meilleure réponse, les mutations créent des effets secondaires
Matt Lo
1
Par curiosité, pourquoi ne pas simplement revenir arrayimmédiatement si fromIndex === toIndexet ne créer le newArraysi ce n'est pas le cas? L'immuabilité ne signifie pas qu'une nouvelle copie doit être créée par appel de fonction, même en l'absence de changement. Le simple fait de demander à b / c le motif de l'augmentation de la longueur de cette fonction (par rapport aux lignes isolées basées sur les épissures) est la performance, et fromIndexpeut souvent être égal toIndex, en fonction de l'utilisation.
Robert Monfera
5

J'avais besoin d'une méthode de déplacement immuable (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 l'épissure.

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 .

Javid Jamae
la source
Il est toujours bon de voir ppl prendre en compte les mutations.
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
la source
2

J'ai fini par combiner deux d'entre eux pour travailler un peu mieux lors de déplacements sur de petites et grandes distances. J'obtiens des résultats assez cohérents, mais cela pourrait probablement être modifié un peu par quelqu'un de plus intelligent que moi pour travailler différemment pour différentes tailles, etc.

Utiliser certaines des autres méthodes pour déplacer des objets sur de petites distances était beaucoup plus rapide (x10) que d'utiliser une épissure. Cela peut changer en fonction de la longueur des tableaux, mais c'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

Andrew Backer
la source
2

Il est dit à de nombreux endroits ( ajouter des fonctions personnalisées dans Array.prototype ) que jouer avec le prototype Array pourrait être une mauvaise idée, de toute façon j'ai combiné le meilleur de divers articles, je suis venu avec ceci, en utilisant Javascript moderne:

    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

BernieSF
la source
2

Cette version n'est pas idéale à toutes fins, et tout le monde n'aime pas les expressions virgule, mais voici une ligne unique 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 améliorée des performances renvoie le tableau d'entrée si aucun déplacement n'est nécessaire, c'est toujours OK pour une utilisation immuable, car le tableau ne changera pas, et c'est toujours 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 diffusion pour générer une nouvelle copie. L'utilisation d'une arité fixe 3 movemettrait en péril la propriété d'expression unique, ou la nature non destructive, ou l'avantage de performance de splice. Encore une fois, il s'agit davantage d'un exemple qui répond à certains critères que d'une suggestion d'utilisation en production.

Robert Monfera
la source
1

Array.move.js

Sommaire

Déplace les é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 les éléments. S'il est négatif, l' indice commencera à la fin.

howMany : nombre d'éléments à déplacer de l' index .

toIndex : Index du tableau sur lequel placer les éléments déplacés. S'il est 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;
    }
});
Jonathan Neal
la source
2
Bien que cela .movesemble fonctionner (je ne l'ai pas testé), vous devez noter qu'il ne fait partie d'aucune norme. Il est également bon d'avertir les gens que les fonctions polyfill / monkeypatched peuvent casser du code qui suppose que tout ce qui est énumérable est le leur.
Jeremy J Starcher du
1
a = ["a", "b", "c"]; a.move (0,1,1); // a = ["a", "b", "c"], devrait être ["b", "a", "c"]
Leonard Pauli
2
Cette fonctionnalité est obsolète et peut ne plus être prise en charge. Soyez prudent Voir: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mostafa
1

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

J'ai réalisé cela en changeant le cas pour 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
la source
1

En complément de l'excellente réponse de Reid (et parce que je ne peux pas faire de commentaire); Vous pouvez utiliser modulo pour faire "survoler" des indices négatifs et des indices 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)); 

Ai-je
la source
Oui - étant donné que les indices négatifs sont pris en charge, il semble judicieux d'envelopper des indices trop grands plutôt que d'insérer des valeurs indéfinies, à mon avis.
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
la source
1

Je pensais que c'était un problème de swap mais ce n'est pas le cas. Voici ma solution monoplace:

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

Voici un petit 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' ]
cagdas_ucar
la source
Eh bien, la question n'était pas de permuter des éléments. L'auteur a demandé une solution pour une stratégie d'insertion.
Andreas Dolk
En ce qui concerne la question à l'examen, c'est objectivement la mauvaise réponse.
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 );

résultat:

["b", "a", "c", "d"]
Naycho334
la source
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
la source
1
Bienvenue chez SO! Il y a 21 réponses supplémentaires ... alors, s'il vous plaît, ne vous contentez pas de placer le code. Expliquez l'avantage de votre réponse.
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);

Mohd Abdul Baquee
la source
0

Version immuable sans copie de tableau:

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
la source
0

Je pense que la meilleure façon est de définir une nouvelle propriété pour les tableaux

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
la source
0

Une autre variante JS pure utilisant l'opérateur de propagation de tableau ES6 sans mutation

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
la source
0

Cette méthode préservera le tableau d'origine et vérifiera les erreurs de délimitation.

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
la source