Przenieś element tablicy z jednej pozycji tablicy na drugą

522

Trudno mi wymyślić, jak przenieść element tablicy. Na przykład biorąc pod uwagę następujące kwestie:

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

Jak mogę napisać funkcję, aby przenieść się 'd'wcześniej 'b'?

Czy 'a'po 'c'?

Po przeprowadzce wskaźniki pozostałych elementów powinny zostać zaktualizowane. Oznacza to, że w pierwszym przykładzie po przeniesieniu arr [0] będzie = 'a', arr [1] = 'd' arr [2] = 'b', arr [3] = 'c', arr [4] = 'mi'

Wygląda na to, że powinno to być całkiem proste, ale nie mogę się wokół tego owinąć.

Mark Brown
źródło
3
cóż, to pytanie stare, ale złote
Jalal
za pomocą ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa
To po prostu zamienia elementy na initi target.
Matt F.

Odpowiedzi:

671

Jeśli chcesz wersję na npm, przeniesienie tablicy jest najbliższe tej odpowiedzi, chociaż nie jest to ta sama implementacja. Aby uzyskać więcej informacji, zobacz sekcję dotyczącą jego użytkowania. Poprzednią wersję tej odpowiedzi (która zmodyfikowała Array.prototype.move) można znaleźć na npm w array.prototype.move .


Miałem całkiem dobry sukces dzięki tej funkcji:

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

Pamiętaj, że ostatni returnsłuży wyłącznie do celów testowych: splicewykonuje operacje na tablicy w miejscu, więc zwrot nie jest konieczny. W związku z tym movejest to operacja lokalna. Jeśli chcesz tego uniknąć i zwrócić kopię, użyj slice.

Przechodzenie przez kod:

  1. Jeśli new_indexjest większa niż długość tablicy, chcemy (przypuszczam), aby poprawnie wypełnić tablicę nowymi undefineds. Ten mały fragment zajmuje się tym, popychając undefinedtablicę, aż uzyskamy odpowiednią długość.
  2. Następnie arr.splice(old_index, 1)[0]łączymy stary element. splicezwraca element, który został wycięty, ale jest w tablicy. W naszym powyższym przykładzie tak było [1]. Więc bierzemy pierwszy indeks tej tablicy, aby uzyskać 1tam surowe dane .
  3. Następnie używamy, spliceaby wstawić ten element w miejsce new_index. Ponieważ wstawiliśmy powyższą tablicę, jeśli new_index > arr.lengthprawdopodobnie pojawi się ona we właściwym miejscu, chyba że zrobili coś dziwnego, jak podanie liczby ujemnej.

Bardziej wymyślna wersja uwzględniająca ujemne wskaźniki:

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

Które powinny uwzględniać takie rzeczy jak array_move([1, 2, 3], -1, -2)poprawnie (przenieś ostatni element na drugie na ostatnie miejsce). Wynik powinien być [1, 3, 2].

Tak czy inaczej, w swoim pierwotnym pytaniu, byś zrobił array_move(arr, 0, 2)dla aPO c. Dla dprzed b, byś zrobił array_move(arr, 3, 1).

Reid
źródło
19
To działa idealnie! Twoje wyjaśnienie jest bardzo jasne. Dziękujemy za poświęcenie czasu na napisanie tego.
Mark Brown
16
Nie należy manipulować prototypami Object i Array, ponieważ powoduje to problemy podczas iteracji elementów.
burak emre
9
@burakemre: Myślę, że wniosek nie jest tak jasno wyciągnięty. Większość dobrych programistów JS (i najpopularniejsze biblioteki) użyje .hasOwnPropertytestu podczas iteracji z rzeczami takimi jak for..in, szczególnie z bibliotekami takimi jak Prototype i MooTools, które modyfikują prototypy. W każdym razie nie czułem, że jest to szczególnie ważna kwestia w stosunkowo ograniczonym przykładzie, takim jak ten, i istnieje spory podział w społeczności, czy modyfikacja prototypu jest dobrym pomysłem. Zwykle jednak problemy z iteracją stanowią najmniejszy problem.
Reid
3
Pętla w kroku 1 nie jest potrzebna, wystarczy użyć this[new_index] = undefined;w ifbloku. Ponieważ tablice JavaScript są rzadkie, zwiększy to rozmiar tablicy, aby uwzględnić new_index, .spliceaby działał, ale bez konieczności tworzenia jakichkolwiek elementów pośrednich.
Michael
3
@Michael: Dobra uwaga - ale robienie tego this[new_index] = undefinedspowoduje umieszczenie znaku undefinedw gnieździe tablicy przed właściwym indeksem. (Np. [1,2,3].move(0,10)Będzie miał 1w gnieździe 10 i undefinedw gnieździe 9.) Raczej, jeśli rzadkość jest OK, moglibyśmy obejść się this[new_index] = this.splice(old_index, 1)[0]bez drugiego wywołania splicingu ( zamiast tego należy ustawić if / else).
Reid
268

Oto jedna linijka, którą znalazłem na JSPerf ....

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

co jest niesamowite do czytania, ale jeśli chcesz wydajności (w małych zestawach danych) spróbuj ...

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

Nie mogę uwierzyć , to wszystko powinno iść do Richarda Scarrotta . W tym teście wydajności pokonuje metodę opartą na spawaniu dla mniejszych zestawów danych . Jest to jednak znacznie wolniejsze na większych zestawach danych, jak podkreśla Darwayne .

digiguru
źródło
2
Bardziej wydajne rozwiązanie działa wolniej w przypadku dużych zbiorów danych. jsperf.com/array-prototype-move/8
Darwayne
44
To wydaje się bardzo głupie. Wydajność małych zestawów danych jest znikomym zyskiem, ale utrata dużych zestawów danych jest znaczną stratą. Twoja wymiana netto jest ujemna.
Kyeotic
3
@Reid To nie był wymóg. IMO można założyć, że długość tablicy nie ulega modyfikacji.
robsch
3
Jedno liniowe rozwiązanie musi obsługiwać dwie sytuacje:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L
13
Nigdy nie modyfikuj wbudowanych prototypów. nczonline.net/blog/2010/03/02/…
LJHarb
230

Lubię to. Jest zwięzły i działa.

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

Uwaga: zawsze pamiętaj o sprawdzeniu granic tablicy.

Uruchom Snippet na jsFiddle

SteakOverflow
źródło
29
Ponieważ Array.splice zwraca usunięte wartości w nowej macierzy, możesz zapisać je jako jedną linijkę ... arr.splice (indeks + 1, 0, arr.splice (indeks, 1) [0]);
Eric
49
Osobiście wolę 3-liniowy kod. Łatwiej to zrozumieć: Uzyskaj kopię elementu; usuń go z tablicy; wstaw go w nowej pozycji. Jedna linijka jest krótsza, ale nie tak zrozumiała dla innych ludzi ...
Filip
2
Krótki i prosty kod. Ale to 2019 !!, Stwórz klon tablicy i zwróć go zamiast mutować tablicę. Dzięki temu twoja funkcja „arraymove” będzie zgodna ze standardami programowania funkcjonalnego
SamwellTarly
4
OK, ale nie wszystko, co nie jest ani musi być funkcjonalny programowania zgodny; plus to może być nadal przydatne w programowaniu funkcjonalnym wewnątrz procedury manipulowania tablicami lokalnymi.
SteakOverflow,
36

Metoda splice () dodaje / usuwa elementy do / z tablicy i zwraca usunięte elementy.

Uwaga: Ta metoda zmienia oryginalną tablicę. / 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"]

ponieważ funkcja jest łańcuchowa, działa to również:

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

demo tutaj


źródło
Czy jest jakaś biblioteka, która tego używa? Całkiem schludnie!
uicoded
Zobacz inne komentarze na ten temat: modyfikowanie wbudowanych prototypów, takich jak Array i Object, jest złym pomysłem. Zniszczysz rzeczy.
geoidesic
27

Mój 2c. Łatwy do odczytania, działa, jest szybki, nie tworzy nowych tablic.

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
źródło
2
Pierwszy ciąg funkcji powinieneś powrócić array, tak jak to zrobiono na końcu.
Siergiej Woroneżski
3
Prawda, jak mi tego brakowało? Naprawiony!
Merc
Najbardziej podoba mi się twoje proste i elastyczne rozwiązanie. Dzięki!
Roman M. Koss,
18

Wpadłem na pomysł @Reid pchania czegoś w miejsce elementu, który ma zostać przesunięty, aby utrzymać stały rozmiar tablicy. To upraszcza obliczenia. Ponadto pchanie pustego obiektu daje dodatkowe korzyści wynikające z możliwości jego unikatowego wyszukiwania później. Działa to, ponieważ dwa obiekty nie są równe, dopóki nie odnoszą się do tego samego obiektu.

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

Oto funkcja, która pobiera tablicę źródłową i indeksy źródłowe, docelowe. W razie potrzeby możesz dodać go do Array.prototype.

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
źródło
1
To wygląda obiecująco ... i nie wiedziałem tego o porównaniach javascript js. Dzięki!
Mark Brown
Nie działa w przypadku sourceIndex = 0,destIndex = 1
Siergiej Woroneżski
destIndexma być indeksem przed przeniesieniem elementu źródłowego do tablicy.
Anurag
To najlepsza jak dotąd odpowiedź. Inne odpowiedzi zawiodły parę testów jednostkowych w moim apartamencie (ruchomy obiekt do przodu)
Ilja Iwanow
16

Jest to oparte na rozwiązaniu @ Reid. Z wyjątkiem:

  • Nie zmieniam Arrayprototypu.
  • Przesunięcie przedmiotu poza granicę w prawo nie tworzy undefinedprzedmiotów, po prostu przesuwa je do pozycji najbardziej po prawej.

Funkcjonować:

function move(array, oldIndex, newIndex) {
    if (newIndex >= array.length) {
        newIndex = array.length - 1;
    }
    array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
    return array;
}

Testy jednostkowe:

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
źródło
to źle, jeśli wstawisz pozycję posta, indeks zmieni się, ponieważ usunąłeś pozycję
Yao Zhao
Dziękuję Ci. Chciałem usunąć element z tablicy bez pozostawiania elementu zerowego (który wystąpił podczas używania splice (indexToRemove). Użyłem twojej metody, aby przenieść element, który chciałem usunąć na koniec tablicy, a następnie użyłem pop () metoda usunięcia
Luke Schoen
podoba mi się funkcja „przenieś element do skrajnej prawej pozycji”, przydatna w moim przypadku. thx
bFunc
11

Oto moje jednoelementowe rozwiązanie ES6 z opcjonalnym parametrem on.

if (typeof Array.prototype.move === "undefined") {
  Array.prototype.move = function(from, to, on = 1) {
    this.splice(to, 0, ...this.splice(from, on))
  }
}

Dostosowanie pierwszego rozwiązania zaproponowanego przez digiguru

Ten parametr onokreśla liczbę elementów zaczynających się od fromprzeniesienia.

Elie Teyssedou
źródło
Rozwiązanie jest w porządku. Jednak po rozwinięciu prototypu nie należy używać funkcji strzałki, ponieważ w tym przypadku „to” nie jest instancją tablicy, ale na przykład obiektem Window.
wawka
7

Jednym z podejść byłoby utworzenie nowej tablicy z kawałkami w żądanej kolejności, przy użyciu metody wycinania.

Przykład

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) daje [„a”]
  • arr.slice (2,4) daje ci ['b', 'c']
  • arr.slice (4) daje ci ['e']
Jared Updike
źródło
1
Zdajesz sobie sprawę z tego arr2, że w wyniku operacji konkatenacji twoja postać jest ciągiem, prawda? :) W końcu jest "adc,de".
Ken Franqueiro
6

spliceMetoda Arraymoże pomóc: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Pamiętaj, że może to być stosunkowo drogie, ponieważ musi aktywnie ponownie indeksować tablicę.

Ken Franqueiro
źródło
Tak, ale jak tylko wykonam połączenie, indeksy tablicy są aktualizowane, co utrudnia mi określenie, gdzie umieścić element, który właśnie usunąłem. Zwłaszcza, że ​​potrzebuję tej funkcji, aby móc obsługiwać ruchy w obu kierunkach.
Mark Brown,
@Zaznacz: nie dziel łańcucha i nie zapisuj go w tej samej zmiennej, utwórz nowy łańcuch i złóż go. Zobacz moją odpowiedź poniżej.
Jared Updike
6

Możesz zaimplementować podstawowy rachunek różniczkowy i stworzyć uniwersalną funkcję do przenoszenia elementu tablicy z jednej pozycji do drugiej.

W przypadku JavaScript wygląda to tak:

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; 

}

Sprawdź „ruchome elementy tablicy” w „gloommatter”, aby uzyskać szczegółowe wyjaśnienia.

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

Andrea
źródło
1
To powinna być poprawna odpowiedź, ponieważ nie przydziela nowych tablic. Dzięki!
Cᴏʀʏ
Link jest zepsuty.
Rokit
6

Wdrożyłem niezmienne ECMAScript 6rozwiązanie oparte na @Mercodpowiedzi tutaj:

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

Nazwy zmiennych można skracać, po prostu używać długich, aby kod mógł się wyjaśnić.

Barry Michael Doyle
źródło
zdecydowanie lepsza odpowiedź, mutacje wywołują skutki uboczne
Matt Lo,
1
Z ciekawości może po prostu wrócić array natychmiast fromIndex === toIndex i stworzyć tylko, newArrayjeśli tak nie jest? Niezmienność nie oznacza, że ​​należy utworzyć jedną nową kopię na wywołanie funkcji, nawet jeśli nie ma zmian. Samo pytanie b / c motywem zwiększonej długości tej funkcji (w stosunku do jedno-liniowych splicingu) jest wydajność i fromIndexmoże często być równa toIndex, w zależności od zastosowania.
Robert Monfera
5

Potrzebowałem niezmiennej metody przenoszenia (takiej, która nie zmieniła oryginalnej tablicy), więc dostosowałem zaakceptowaną odpowiedź @ Reida, aby po prostu użyć Object.assign do utworzenia kopii tablicy przed wykonaniem łączenia.

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

Oto jsfiddle pokazujący go w akcji .

Javid Jamae
źródło
Zawsze dobrze jest widzieć, że ppl bierze pod uwagę mutacje.
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
źródło
2

Skończyło się na tym, że połączyłem dwa z nich, aby działać trochę lepiej, gdy poruszam się zarówno na małe, jak i duże odległości. Otrzymuję dość spójne wyniki, ale prawdopodobnie ktoś bystrzejszy ode mnie może go trochę ulepszyć, aby działał inaczej dla różnych rozmiarów itp.

Korzystanie z niektórych innych metod podczas przemieszczania obiektów na małe odległości było znacznie szybsze (x10) niż stosowanie łączenia. Może się to zmieniać w zależności od długości macierzy, ale dotyczy to dużych tablic.

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
źródło
2

W wielu miejscach ( dodawanie niestandardowych funkcji do Array.prototype ) granie z prototypem Array może być złym pomysłem, w każdym razie połączyłem najlepsze z różnych postów, przyszło mi to przy użyciu nowoczesnego Javascript:

    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

Nadzieja może być przydatna dla każdego

BernieSF
źródło
2

Ta wersja nie jest idealna do wszystkich celów i nie wszyscy lubią wyrażenia przecinkowe, ale tutaj jest jeden wiersz, który jest czystym wyrażeniem, tworząc nową kopię:

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

Wersja nieco poprawiona pod względem wydajności zwraca tablicę wejściową, jeśli nie jest wymagany żaden ruch, jest nadal OK dla niezmiennego użycia, ponieważ tablica się nie zmieni, a to wciąż czyste wyrażenie:

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

Wywołanie któregokolwiek z nich jest

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

tzn. polega na rozpowszechnianiu w celu wygenerowania nowej kopii. Użycie stałej arity 3 movezagroziłoby albo właściwości pojedynczego wyrażenia, albo nieniszczącej naturze, albo korzyści wydajności splice. Ponownie, jest to bardziej przykład spełniający pewne kryteria niż sugestia do zastosowania produkcyjnego.

Robert Monfera
źródło
1

Array.move.js

Podsumowanie

Przenosi elementy wewnątrz tablicy, zwracając tablicę zawierającą przeniesione elementy.

Składnia

array.move(index, howMany, toIndex);

Parametry

indeks : Indeks, w którym należy przenosić elementy. Jeśli ujemna, indeks rozpocznie się od końca.

howMany : liczba elementów do przeniesienia z indeksu .

toIndex : Indeks tablicy, w której należy umieścić przeniesione elementy. Jeśli ujemny, toIndex rozpocznie się od końca.

Stosowanie

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
źródło
2
Chociaż .movewygląda na to, że powinien działać (nie testowałem go), należy pamiętać, że nie jest on częścią żadnego standardu. Dobrze jest też ostrzec ludzi, że funkcje wypełniania / wstawiania małp mogą uszkodzić część kodu, który zakłada, że ​​wszystko, co jest policzalne, należy do nich.
Jeremy J Starcher,
1
a = [„a”, „b”, „c”]; a.move (0,1,1); // a = [„a”, „b”, „c”], powinno być [„b”, ​​„a”, „c”]
Leonard Pauli
2
Ta funkcja jest przestarzała i może już nie być obsługiwana. Uważaj Patrz: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mostafa
1

Użyłem ładnej odpowiedzi @Reid , ale zmagałem się z przenoszeniem elementu z końca tablicy o krok dalej - na początek (jak w pętli ). Np. [„A”, „b”, „c”] powinien stać się [„c”, „a”, „b”] przez wywołanie .move (2,3)

Osiągnąłem to, zmieniając wielkość liter dla 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
źródło
1

Jako dodatek do doskonałej odpowiedzi Reida (i ponieważ nie mogę komentować); Możesz użyć modulo, aby zarówno wskaźniki ujemne, jak i zbyt duże były „najeżdżane”:

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

Czy ja
źródło
Tak - ponieważ obsługiwane są indeksy ujemne, moim zdaniem rozsądne jest zawijanie zbyt dużych indeksów zamiast wstawiania niezdefiniowanych wartości.
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
źródło
1

Myślałem, że to problem wymiany, ale tak nie jest. Oto moje jedno-liniowe rozwiązanie:

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

Oto mały 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
źródło
Cóż, pytanie nie dotyczyło zamiany przedmiotów. Autor poprosił o rozwiązanie dla strategii wstawiania.
Andreas Dolk
Jeśli chodzi o omawiane pytanie, jest to obiektywnie zła odpowiedź.
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 );

wynik:

["b", "a", "c", "d"]
Naycho334
źródło
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
źródło
1
Witamy w SO! Jest 21 dodatkowych odpowiedzi ... więc proszę, nie umieszczaj kodu. Wyjaśnij zalety swojej odpowiedzi.
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
źródło
0

Wersja niezmienna bez kopiowania tablicy:

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
źródło
0

Myślę, że najlepszym sposobem jest zdefiniowanie nowej właściwości tablic

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
źródło
0

Kolejny czysty wariant JS wykorzystujący operator rozprzestrzeniania się macierzy ES6 bez mutacji

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
źródło
0

Ta metoda zachowa oryginalną tablicę i sprawdzi błędy graniczne.

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
źródło