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.
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:
new_index
est supérieur à la longueur du tableau, nous voulons (je présume) remplir correctement le tableau avec le nouveau undefined
s. Ce petit extrait gère cela en poussant undefined
sur le tableau jusqu'à ce que nous ayons la bonne longueur.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.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)
.
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 .
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/
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(','));
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;
}
@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);
}
Ceci est basé sur la solution de @ Reid. Sauf:
Array
.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);
});
});
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.
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) );
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.
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
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 .
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]);
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.
Déplace des éléments dans un tableau, renvoyant un tableau contenant les éléments déplacés.
array.move(index, howMany, toIndex);
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.
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"]
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;
}
});
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;
}
}
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.
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
};
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));
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
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)
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"]