Mover un elemento de matriz de una posición de matriz a otra

522

Me está costando 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 moverme 'd'antes 'b'?

O 'a'despues 'c'?

Después del movimiento, los índices del resto de los elementos deben actualizarse. Esto significa en el primer ejemplo después del movimiento arr [0] would = 'a', arr [1] = 'd' arr [2] = 'b', arr [3] = 'c', arr [4] = 'mi'

Parece que debería ser bastante simple, pero no puedo entenderlo.

Mark Brown
fuente
3
Bueno, esta pregunta es vieja pero dorada
Jalal
usando ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa
Eso solo intercambia los elementos en inity target.
Matt F.

Respuestas:

671

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 el último returnes simplemente para fines de prueba: splicerealiza operaciones en la matriz en el lugar, por lo que no es necesario un retorno. Por extensión, esta movees una operación en el lugar. Si desea evitar eso y devolver una copia, use slice.

Recorriendo el código:

  1. Si new_indexes mayor que la longitud de la matriz, queremos (supongo) rellenar la matriz correctamente con nuevos undefineds. Este pequeño fragmento maneja esto presionando undefinedla matriz hasta que tengamos la longitud adecuada.
  2. Luego, adentro arr.splice(old_index, 1)[0], empalmamos el viejo elemento. splicedevuelve el elemento que se empalmó, pero está en una matriz. En nuestro ejemplo anterior, esto fue [1]. Así que tomamos el primer índice de esa matriz para obtener la materia prima 1allí.
  3. Luego, usamos splicepara insertar este elemento en el lugar del nuevo_índice. Dado que rellenamos la matriz anterior si new_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). Resultado para eso debería ser [1, 3, 2].

De cualquier manera, en su pregunta original, lo haría array_move(arr, 0, 2)para adespués c. Para dantes b, lo harías array_move(arr, 3, 1).

Reid
fuente
19
Esto funciona perfectamente! Y tu explicación es muy clara. Gracias por tomarse el tiempo para escribir esto.
Mark Brown
16
No debe manipular prototipos de objetos y matrices, causa problemas al iterar elementos.
burak emre
99
@burakemre: Creo que esa conclusión no se alcanza tan claramente. La mayoría de los buenos programadores de JS (y las bibliotecas más populares) usarán una .hasOwnPropertyverificación al iterar con cosas como for..in, especialmente con bibliotecas como Prototype y MooTools que modifican los prototipos. De todos modos, no sentí que fuera un tema particularmente importante en un ejemplo relativamente limitado como este, y hay una buena división en la comunidad sobre si la modificación del prototipo es una buena idea. Sin embargo, normalmente los problemas de iteración son la menor preocupación.
Reid
3
No hay necesidad del bucle en el paso 1, simplemente puede usarlo this[new_index] = undefined;dentro del ifbloque. Como las matrices de Javascript son escasas, esto ampliará el tamaño de la matriz para incluir new_index para .spliceque funcione, pero sin necesidad de crear ningún elemento intermedio.
Michael
3
@Michael: Buen punto, pero hacerlo this[new_index] = undefinedrealmente colocará un undefineden la ranura de la matriz antes del índice correcto. (Por ejemplo, [1,2,3].move(0,10)tendrá 1en la ranura 10 y undefineden la ranura 9.) Más bien, si la dispersión está bien, podríamos this[new_index] = this.splice(old_index, 1)[0]prescindir de la otra llamada de empalme (en su lugar, haga un if / else).
Reid
268

Aquí hay un trazador de líneas que encontré en JSPerf ...

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

que es increíble de leer, pero si quieres rendimiento (en pequeños conjuntos de datos) prueba ...

 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 . Es mejor que el método basado en empalmes 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 .

digiguru
fuente
2
Su solución más eficiente es más lenta en grandes conjuntos de datos. jsperf.com/array-prototype-move/8
Darwayne
44
Esto parece un comercio realmente tonto. El rendimiento en conjuntos de datos pequeños es una ganancia insignificante, pero la pérdida en conjuntos de datos grandes es una pérdida significativa. Tu intercambio neto es negativo.
Kyeotic
3
@Reid Eso no era un requisito. En mi opinión, está bien suponer que la longitud de la matriz no se modifica.
robsch
3
Una solución de línea necesita manejar dos situaciones:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L
13
Nunca modifique los prototipos incorporados, nunca. nczonline.net/blog/2010/03/02/…
LJHarb
231

Me gusta de esta manera. Es conciso y funciona.

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

Nota: recuerde siempre verificar los límites de su matriz.

Ejecute Snippet en jsFiddle

Filete
fuente
29
Dado que Array.splice devuelve los valores eliminados en una nueva matriz, puede escribirlo como un trazador de líneas ... arr.splice (index + 1, 0, arr.splice (index, 1) [0]);
Eric
49
Personalmente prefiero el código de 3 líneas. Es más fácil de entender: obtenga una copia del elemento; eliminarlo de la matriz; insertarlo en una nueva posición. El trazador de líneas es más corto pero no tan claro para que otras personas lo entiendan ...
Philipp
2
Código corto y simple. ¡Pero es 2019!, Crea un clon de la matriz y devuélvelo en lugar de mutar la matriz. Esto hará que su función "arraymove" cumpla con los estándares de programación funcional
SamwellTarly
36

El método splice () agrega / elimina elementos a / de 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 encadenable, esto también funciona:

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

demostración aquí


fuente
¿Hay alguna biblioteca que use esto? ¡Con buena pinta!
Uicoded
Vea otros comentarios sobre esto: es una mala idea modificar los prototipos integrados como Array y Object. Romperás cosas.
geodésico
27

Mi 2c. Fácil de leer, funciona, es rápido, no crea nuevas matrices.

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
fuente
2
En la primera cadena de funciones, debe regresar array, como lo hizo al final.
Sergey Voronezhskiy
3
Es cierto, ¿cómo me perdí eso? ¡Fijo!
Merc
Me gusta más su solución simple y flexible. ¡Gracias!
Roman M. Koss
18

Tengo la idea de @Reid de empujar algo en el lugar del elemento que se supone que debe moverse 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 la matriz fuente, 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);
}
Anurag
fuente
1
Esto parece prometedor ... y no sabía eso sobre las comparaciones de JavaScript JS. ¡Gracias!
Mark Brown
No funciona para el caso sourceIndex = 0,destIndex = 1
Sergey Voronezhskiy
destIndexestá destinado a ser el índice antes de que el elemento fuente se mueva en la matriz.
Anurag
Esta es la mejor respuesta hasta ahora. Otras respuestas fallaron un par de pruebas unitarias en mi suite (mover el objeto hacia adelante)
Ilya Ivanov
16

Esto se basa en la solución de @ Reid. Excepto:

  • No estoy cambiando el Arrayprototipo.
  • Mover un elemento fuera de los límites a la derecha no crea undefinedelementos, 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);
    });
});
André Pena
fuente
esto está mal, si inserta una posición de publicación, el índice cambiará ya que ha eliminado el elemento
Yao Zhao
Gracias. Quería eliminar un elemento de una matriz sin dejar un elemento nulo (que ocurrió cuando usé el empalme (indexToRemove). Utilicé su método para mover el elemento que quería eliminar al final de la matriz, y luego usé el pop () método para eliminar.
Luke Schoen
Me gustó la función "mover el elemento a la posición más a la derecha", útil para mi caso. thx
bFunc
11

Aquí está mi solución ES6 one liner con un parámetro opcional on.

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 ones el número de elementos a partir de los fromque desea mover.

Elie Teyssedou
fuente
La solución está bien. Sin embargo, cuando expande un prototipo no debe usar la función de flecha porque en este caso 'esto' no es una instancia de matriz sino, por ejemplo, un objeto Window.
wawka
7

El splicemétodo de Arraypodría ayudar: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Solo tenga en cuenta que puede ser relativamente costoso ya que tiene que volver a indexar activamente la matriz.

Ken Franqueiro
fuente
Sí, pero tan pronto como realizo el empalme, los índices de la matriz se actualizan, lo que me dificulta determinar dónde colocar el elemento que acabo de eliminar. Especialmente porque necesito la función para poder manejar movimientos en ambas direcciones.
Mark Brown
@ Mark: no empalmes la cadena y la guardes en la misma variable, crea una nueva cadena y empalma eso. Vea mi respuesta a continuación.
Jared Updike
7

Un enfoque sería crear una nueva matriz con las piezas en el orden que desee, utilizando el método de corte.

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']
Jared Updike
fuente
1
Te das cuenta de que tu arr2termina siendo una cadena debido a las operaciones de concatenación, ¿verdad? :) Termina siendo "adc,de".
Ken Franqueiro
6

Puede implementar algunos cálculos básicos y crear una función universal para mover elementos 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; 

}

Consulte "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

Andrea
fuente
1
Esta debería ser la respuesta correcta, ya que no asigna nuevas matrices. ¡Gracias!
Cᴏʀʏ
El enlace está roto.
Rokit
6

He implementado una ECMAScript 6solución inmutable basada en @Mercla respuesta de 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 por sí mismo.

Barry Michael Doyle
fuente
definitivamente una mejor respuesta, las mutaciones crean efectos secundarios
Matt Lo
1
Por curiosidad, ¿por qué no simplemente regresar arrayinmediatamente si fromIndex === toIndex, y solo crear el newArraysi no es el caso? La inmutabilidad no significa que se deba crear una copia nueva por llamada a la función, incluso cuando no haya cambios. Solo preguntarle a b / c el motivo del aumento de la longitud de esta función (en relación con las líneas simples basadas en empalmes) es el rendimiento, y a fromIndexmenudo puede ser igual toIndex, dependiendo del uso.
Robert Monfera
5

Necesitaba un método de movimiento inmutable (uno que no cambiara la matriz original), así que adapté la respuesta aceptada de @ Reid para simplemente usar 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 .

Javid Jamae
fuente
Siempre es bueno ver a las personas teniendo en cuenta las mutaciones.
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
fuente
2

Terminé combinando dos de estos para trabajar un poco mejor al mover distancias pequeñas y grandes. Obtengo resultados bastante consistentes, pero esto probablemente podría ser modificado 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 al mover objetos a distancias pequeñas fue significativamente más rápido (x10) que el uso del empalme. Sin embargo, esto puede 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;    
    }
}

http://jsperf.com/arraymove-many-sizes

Andrew Backer
fuente
2

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 Javascript moderno:

    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

La esperanza puede ser útil para cualquiera

BernieSF
fuente
2

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 frase que es pura expresión, creando una copia nueva:

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

Una versión ligeramente mejorada de rendimiento devuelve la matriz de entrada si no se necesita mover, todavía está bien para un uso inmutable, ya que la matriz no cambiará, y sigue siendo una expresión pura:

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

La invocación de cualquiera es

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

es decir, se basa en la difusión para generar una copia nueva. El uso de una aridad fija 3 movepondría en peligro la propiedad de expresión única, o la naturaleza no destructiva, o el beneficio de rendimiento de splice. Nuevamente, es más un ejemplo que cumple algunos criterios que una sugerencia para el uso de producción.

Robert Monfera
fuente
1

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

index : índice en el que mover elementos. Si es negativo, el índice comenzará desde el final.

howMany : número de elementos para mover desde el índice .

toIndex : índice de la matriz en la que colocar 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"]

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
fuente
2
Si bien .moveparece que debería funcionar (no lo he probado), debe tener en cuenta que no es parte de ningún estándar. También es bueno advertir a la gente que las funciones polyfill / monkeypatched pueden romper algún código que asume que todo lo enumerable es suyo.
Jeremy J Starcher
1
a = ["a", "b", "c"]; a.move (0,1,1); // a = ["a", "b", "c"], debe ser ["b", "a", "c"]
Leonard Pauli
2
Esta característica es obsoleta y es posible que ya no sea compatible. Tenga cuidado Consulte: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mostafa
1

Utilicé la buena respuesta de @Reid , pero tuve problemas 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)

Lo logré cambiando el caso 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
    };
Marcel Böttcher
fuente
1

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 "pasen":

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

Hice
fuente
Sí, dado que los índices negativos son compatibles, parece sensato ajustar índices demasiado grandes en lugar de insertar valores indefinidos, en mi opinión.
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
fuente
1

Pensé que esto era un problema de intercambio, pero no lo es. Aquí está mi solución de una línea:

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

Aquí hay una pequeña prueba:

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
fuente
Bueno, la pregunta no era sobre el intercambio de artículos. El autor solicitó una solución para una estrategia de inserción.
Andreas Dolk
Con respecto a la pregunta en cuestión, esta es objetivamente la respuesta incorrecta.
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 );

resultado:

["b", "a", "c", "d"]
Naycho334
fuente
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
fuente
1
Bienvenido a SO! Hay 21 respuestas adicionales ... así que, por favor, no solo coloque el código. Explica el beneficio de tu respuesta.
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
fuente
0

Versión inmutable sin copia de matriz:

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
fuente
0

Creo que la mejor manera es definir una nueva propiedad para Arrays

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
fuente
0

Otra variante pura de JS que usa el operador de propagación de matriz ES6 sin mutación

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
fuente
0

Este método preservará la matriz original y verificará los errores delimitadores.

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
fuente