objetos - javascript indexof array of objects
Mueva un elemento de matriz de una posiciĆ³n de matriz a otra (20)
Me resulta difícil descubrir cómo mover un elemento de matriz. Por ejemplo, dado lo siguiente:
var arr = [ ''a'', ''b'', ''c'', ''d'', ''e''];
¿Cómo puedo escribir una función para mover ''d''
antes de ''b''
?
O ''a''
después de ''c''
?
Después de la mudanza, los índices del resto de los elementos deben actualizarse. Esto significa que en el primer ejemplo después del movimiento arr [0] sería = ''a'', arr [1] = ''d'' arr [2] = ''b'', arr [3] = ''c'', arr [4] = ''mi''
Parece que debería ser bastante simple, pero no puedo rodearlo con la cabeza.
El método splice () agrega / elimina elementos a / desde una matriz y devuelve los elementos eliminados .
Nota: Este método cambia la matriz 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"]
como la función es chainable esto funciona también:
alert(arr.move(0,2).join('',''));
Array.move.js
Resumen
Mueve elementos dentro de una matriz, devolviendo una matriz que contiene los elementos movidos.
Sintaxis
array.move(index, howMany, toIndex);
Parámetros
índice : índice al que mover elementos. Si es negativo, el índice comenzará desde el final.
howMany : Número de elementos a mover desde el índice .
Índice de índice: índice de la matriz en la que se colocan los elementos movidos. Si es negativo, toIndex comenzará desde el final.
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"]
Relleno de polietileno
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;
}
});
Aquí está mi solución ES6 de línea única con un parámetro opcional activado.
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
this.splice(to, 0, ...this.splice(from, on))
}
}
Adaptación de la primera solución propuesta por digiguru
El parámetro on
es el número de elementos a partir from
que desea mover.
Aquí hay un forro que encontré en JSPerf ...
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
que es impresionante de leer, pero si desea rendimiento (en pequeños conjuntos de datos) intente ...
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;
}
}
No puedo tomar ningún crédito, todo debería ir a Richard Scarrott . Supera el método basado en el empalme para conjuntos de datos más pequeños en esta prueba de rendimiento . Sin embargo, es significativamente más lento en conjuntos de datos más grandes, como señala Darwayne .
Como una adición a la excelente respuesta de Reid (y porque no puedo comentar); Puede usar el módulo para hacer que tanto los índices negativos como los índices demasiado grandes "rueden":
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));
El método de splice
de Array
podría ayudar: splice
Solo tenga en cuenta que puede ser relativamente caro, ya que tiene que volver a indexar activamente la matriz.
Esta versión no es ideal para todos los propósitos, y no a todos les gustan las expresiones de coma, pero aquí hay una sola frase que es una expresión pura, creando una copia nueva:
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
Una versión ligeramente mejorada en el rendimiento devuelve la matriz de entrada si no se necesita un movimiento, todavía está bien para uso inmutable, ya que la matriz no cambiará, y aún es una expresión pura:
const move = (from, to, ...a) => (a.splice(to, 0, ...a.splice(from, 1)), a)
La invocación de cualquiera de ellas es
const move = (from, to, ...a) =>
from === to
? a
: (a.splice(to, 0, ...a.splice(from, 1)), a)
es decir, se basa en la difusión para generar una copia nueva. El uso de un move
arity 3 fijo pondría en peligro la propiedad de una sola expresión, la naturaleza no destructiva o el beneficio de rendimiento del splice
. Nuevamente, es más un ejemplo que cumple algunos criterios que una sugerencia para el uso en producción.
Esto se basa en la solución de @ Reid. Excepto:
- No estoy cambiando el prototipo
Array
. - Mover un elemento fuera de los límites a la derecha no crea elementos no
undefined
, solo mueve el elemento a la posición más a la derecha.
Función:
function move(array, oldIndex, newIndex) {
if (newIndex >= array.length) {
newIndex = array.length - 1;
}
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
return array;
}
Pruebas unitarias:
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);
});
});
He implementado una solución ECMAScript 6
inmutable basada en la respuesta de @Merc
aquí:
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;
};
Los nombres de las variables se pueden acortar, solo se usan los largos para que el código pueda explicarse.
Me gusta de esta manera. Funciona, es rápido y elegante.
function arraymove(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
Nota: siempre recuerde verificar los límites de su matriz.
Mi 2c. Fácil de leer, funciona, es rápido, no crea nuevos arreglos.
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;
}
Necesitaba un método de movimiento inmutable (uno que no cambiara la matriz original), así que adapté la respuesta aceptada de @ Reid para usar simplemente Object.assign para crear una copia de la matriz antes de hacer el empalme.
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;
};
Aquí hay un jsfiddle que lo muestra en acción .
Obtuve esta idea de @Reid de empujar algo en el lugar del elemento que se debe mover para mantener constante el tamaño de la matriz. Eso simplifica los cálculos. Además, empujar un objeto vacío tiene los beneficios adicionales de poder buscarlo de manera única más adelante. Esto funciona porque dos objetos no son iguales hasta que se refieren al mismo objeto.
({}) == ({}); // false
Así que aquí está la función que toma en la matriz de origen, y la fuente, los índices de destino. Puede agregarlo al Array.prototype si es necesario.
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);
}
Puede implementar algunos cálculos básicos y crear una función universal para mover el elemento de matriz de una posición a otra.
Para JavaScript se ve así:
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;
}
Echa un vistazo a "elementos de matriz en movimiento" en "gloommatter" para obtener una explicación detallada.
http://www.gloommatter.com/DDesign/programming/moving-any-array-elements-universal-function.html
Se dice que en muchos lugares ( agregar funciones personalizadas en Array.prototype ) jugar con el prototipo Array podría ser una mala idea, de todos modos, combiné lo mejor de varias publicaciones, vine con esto, usando el Javascript moderno:
const shuffled = move(fromIndex, toIndex, ...list)
La esperanza puede ser de utilidad para cualquiera.
Si desea una versión en npm, array-move es la más cercana a esta respuesta, aunque no es la misma implementación. Vea su sección de uso para más detalles. La versión anterior de esta respuesta (que modificó Array.prototype.move) se puede encontrar en npm en array.prototype.move .
Tuve bastante éxito con esta función:
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));
Tenga en cuenta que la última return
es simplemente para fines de prueba: el splice
realiza operaciones en la matriz en el lugar, por lo que no es necesario realizar una devolución. Por extensión, este move
es una operación in situ. Si quiere evitar eso y devolver una copia, use slice
.
Pasando por el código:
- Si
new_index
es mayor que la longitud de la matriz, queremos (supongo) que la matriz se rellene correctamente con nuevos sundefined
. Este pequeño fragmento maneja esto presionandoundefined
en la matriz hasta que tengamos la longitud adecuada. - Luego, en
arr.splice(old_index, 1)[0]
,arr.splice(old_index, 1)[0]
el elemento antiguo.splice
devuelve el elemento que fue empalmado, pero está en una matriz. En nuestro ejemplo anterior, esto fue[1]
. Entonces tomamos el primer índice de esa matriz para obtener el1
bruto allí. - Luego usamos
splice
para insertar este elemento en el lugar de new_index. Ya quenew_index > arr.length
la matriz anterior sinew_index > arr.length
, probablemente aparecerá en el lugar correcto, a menos que hayan hecho algo extraño, como pasar un número negativo.
Una versión más elegante para tener en cuenta los índices negativos:
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));
Lo que debería tener en cuenta cosas como array_move([1, 2, 3], -1, -2)
correctamente (mover el último elemento al segundo al último lugar). El resultado para eso debe ser [1, 3, 2]
.
De cualquier manera, en tu pregunta original, harías array_move(arr, 0, 2)
para después de c
. Para d
antes de b
, harías array_move(arr, 3, 1)
.
Terminé combinando dos de estos para trabajar un poco mejor al mover distancias pequeñas y grandes. Obtengo resultados bastante consistentes, pero esto podría ser ajustado un poco por alguien más inteligente que yo para trabajar de manera diferente para diferentes tamaños, etc.
El uso de algunos de los otros métodos para mover objetos a pequeñas distancias fue significativamente más rápido (x10) que usar el empalme. Sin embargo, esto podría cambiar dependiendo de la longitud de la matriz, pero es cierto para matrices grandes.
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;
}
}
Un enfoque sería crear una nueva matriz con las piezas en el orden que desee, utilizando el método de división.
Ejemplo
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) te da [''a'']
- arr.slice (2,4) te da [''b'', ''c'']
- arr.slice (4) te da [''e'']
Usé la buena respuesta de @Reid , pero luché para mover un elemento desde el final de una matriz un paso más allá, hasta el principio (como en un bucle ). Por ejemplo, [''a'', ''b'', ''c''] debería convertirse en [''c'', ''a'', ''b''] llamando a .move (2,3)
Logré esto cambiando el caso para 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
};
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]);