한 배열 위치에서 다른 배열 위치로 배열 요소 이동


522

배열 요소를 이동하는 방법을 알아내는 데 어려움을 겪고 있습니다. 예를 들면 다음과 같습니다.

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

'd'이전 에 이동할 함수를 작성하려면 어떻게 해야 'b'합니까?

또는 'a''c'?

이동 후 나머지 요소의 색인을 업데이트해야합니다. 이것은 이동 후 첫 번째 예에서 arr [0] = 'a', arr [1] = 'd'arr [2] = 'b', arr [3] = 'c', arr [4] = '이자형'

이것은 매우 간단해야하지만 머리를 감쌀 수는 없습니다.


3
Jalal

const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa

그러면 init및 의 요소 만 교체 target됩니다.
Matt F.

답변:


671

npm의 버전을 원한다면 array-move 가이 답변에 가장 가깝지만 동일한 구현은 아닙니다. 자세한 내용은 사용법 섹션을 참조하십시오. 이 답변의 이전 버전 (Array.prototype.move 수정)은 npm에서 찾을 수 있습니다. array.prototype.move .


이 기능으로 상당히 성공했습니다.

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

마지막 return은 테스트 목적으로 만 사용됩니다. splice어레이에서 적절한 작업을 수행하므로 반환 할 필요가 없습니다. 확장하면이 작업 move은 적절한 작업입니다. 이를 피하고 사본을 반환하려면을 사용하십시오 slice.

코드 단계별 설명 :

  1. 경우 new_index새와 배열이 제대로 패드에 배열, 우리는 (나는 가정) 원하는 길이보다 큰 경우undefined 의. 이 작은 스 니펫 undefined은 적절한 길이가 될 때까지 배열 을 밀어서이를 처리합니다 .
  2. 그런 다음 arr.splice(old_index, 1)[0]에서 이전 요소를 연결합니다. splice접합 된 ​​요소를 반환하지만 배열에 있습니다. 위의 예에서 이것은이었습니다 [1]. 따라서 해당 배열의 첫 번째 인덱스를 가져 와서1 .
  3. 그런 다음 splice이 요소를 new_index의 위치에 삽입합니다. 위의 배열을 위의 패딩했기 때문에 new_index > arr.length음수로 전달하는 것과 같은 이상한 일을하지 않는 한 올바른 위치에 나타납니다.

음수 지수를 설명하는 더 멋진 버전 :

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

어느 것이 array_move([1, 2, 3], -1, -2)올바르게 설명해야합니까 (마지막 요소를 두 번째 위치에서 마지막 위치로 이동). 그 결과는[1, 3, 2] .

어느 쪽이든, 원래의 질문에, 당신은 할 것이다 array_move(arr, 0, 2)위한 ac. 들어 d전에 b, 당신이 할 것입니다 array_move(arr, 3, 1).


19
이것은 완벽하게 작동합니다! 그리고 당신의 설명은 매우 분명합니다. 시간을내어 작성해 주셔서 감사합니다.
Mark Brown

16
Object 및 Array 프로토 타입을 조작해서는 안되며, 요소를 반복 할 때 문제가 발생합니다.
burak emre

9
@ burakemre : 결론에 도달하지 못했다고 생각합니다. 가장 좋은 JS 프로그래머 (및 가장 인기있는 라이브러리)는 .hasOwnPropertyfor..in과 같은 것들, 특히 프로토 타입을 수정하는 Prototype 및 MooTools와 같은 라이브러리로 반복 할 때 검사 를 사용합니다 . 어쨌든, 나는 이것이 이와 같이 비교적 제한된 예에서 특히 중요한 문제라고 생각하지 않았으며, 프로토 타입 수정이 좋은 아이디어인지 아닌지에 대해 커뮤니티에서 멋진 분할이 있습니다. 그러나 일반적으로 반복 문제는 가장 큰 문제입니다.
리드

3
1 단계에서 루프가 필요하지 않으므로 블록 this[new_index] = undefined;내에서 간단히 사용할 수 있습니다 if. Javascript 배열이 드물기 때문에 배열 크기를 확장하여 .splice작업을 위해 new_index를 포함 하지만 개입 요소를 만들 필요는 없습니다.
Michael

3
@Michael : 좋은 점-하지만 this[new_index] = undefined실제로 는 올바른 인덱스 앞에undefined 배열 슬롯을 넣습니다 . (예, 해야합니다 슬롯 (10)와 희소 확인을 경우 우리가 할 수있는, 오히려 9 슬롯에) 다른 스플 라이스 호출하지 않고 (그것에게이 경우 / 다른 대신 할). [1,2,3].move(0,10)1undefinedthis[new_index] = this.splice(old_index, 1)[0]
Reid

268

JSPerf에서 찾은 라이너 하나입니다 ...

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

읽는 것이 좋지만 (작은 데이터 세트에서) 성능을 원한다면 시도하십시오 ...

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

크레딧을받을 수 없습니다 . 모두 Richard Scarrott 로 가야합니다 . 이 성능 테스트 에서 더 작은 데이터 세트에 대한 스플 라이스 기반 방법을 능가합니다 . 그러나 Darwayne이 지적한 것처럼 더 큰 데이터 세트 에서는 속도가 상당히 느립니다 .


2
더 큰 성능의 솔루션은 큰 데이터 세트에서 느려집니다. jsperf.com/array-prototype-move/8
Darwayne

44
이것은 정말 바보 같은 거래입니다. 작은 데이터 세트의 성능은 무시할만한 이득이지만 큰 데이터 세트의 손실은 큰 손실입니다. 순 교환은 부정적입니다.
Kyeotic

3
@Reid 그것은 요구 사항이 아니었다. IMO 배열의 길이가 수정되지 않았다고 가정해도됩니다.
robsch

3
한 라인 솔루션은 두 가지 상황을 처리해야합니다.from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L

13
내장 프로토 타입을 절대 수정하지 마십시오. nczonline.net/blog/2010/03/02/…
LJHarb

230

나는이 방법을 좋아한다. 간결하고 작동합니다.

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

참고 : 항상 배열 범위를 확인하십시오.

jsFiddle에서 스 니펫 실행


29
Array.splice는 새 Array에서 제거 된 값을 반환하므로 하나의 라이너로 작성할 수 있습니다. arr.splice (index + 1, 0, arr.splice (index, 1) [0]);
Eric

49
개인적으로 나는 3 줄 코드를 선호합니다. 이해하기가 더 쉽다 : 요소의 사본을 얻는다; 배열에서 제거하십시오. 새 위치에 삽입하십시오. 하나의 라이너는 짧지 만 다른 사람들이 이해하기에는 명확하지 않습니다 ...
Philipp

2
짧고 간단한 코드. 그러나 2019 년입니다! 배열의 복제본을 만들고 배열을 변경하는 대신 반환하십시오. 이렇게하면 함수 "arraymove"가 함수형 프로그래밍 표준을 준수하게됩니다.
SamwellTarly

4
모든 것이 기능 프로그래밍을 준수 할 필요 없습니다 . 또한 이것은 로컬 배열을 조작하는 프로 시저 내부의 함수형 프로그래밍에 여전히 유용 할 수 있습니다.
SteakOverflow 10

36

접합부 () 메소드는 추가 / 배열로부터 /로 항목 및 복귀 제거 제거를 항목 (들).

참고 :이 방법은 원래 배열을 변경합니다. / 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"]

함수가 체인 가능 하므로 다음과 같이 작동합니다.

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

여기 데모


이것을 사용하는 라이브러리가 있습니까? 꽤 깔끔한!
uicoded

이것에 대한 다른 의견을보십시오 : Array 및 Object와 같은 내장 프로토 타입을 수정하는 것은 좋지 않습니다. 당신은 일을 깰 것입니다.
geoidesic

27

내 2c. 읽기 쉽고 작동하며 빠르며 새 배열을 만들지 않습니다.

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

2
함수의 첫 문자열 array에서 끝에서와 같이를 반환해야합니다 .
Sergey Voronezhskiy

3
사실 내가 어떻게 그리웠습니까? 결정된!
Merc

간단하고 유연한 솔루션이 가장 좋습니다. 고마워!
로마 M. 코스

18

배열 크기를 일정하게 유지하기 위해 이동 해야하는 항목 대신 무언가를 밀어 넣는 @Reid의 아이디어를 얻었습니다. 그것은 계산을 단순화합니다. 또한 빈 개체를 밀어 넣으면 나중에 고유하게 검색 할 수 있다는 이점이 있습니다. 두 객체가 동일한 객체를 참조 할 때까지 동일하지 않기 때문에 작동합니다.

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

여기 소스 배열과 소스, 대상 인덱스를 취하는 함수가 있습니다. 필요한 경우 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);
}

1
이것은 유망 해 보입니다 ... 그리고 나는 자바 스크립트 js 비교에 대해 몰랐습니다. 감사!
Mark Brown

Does't이 경우 작동 sourceIndex = 0,destIndex = 1
세르게이 Voronezhskiy에게

destIndex는 소스 요소가 배열에서 이동되기 전에 색인이됩니다.
Anurag

이것이 지금까지 가장 좋은 대답입니다. 다른 답변은 내 스위트 룸에서 몇 가지 단위 테스트에 실패했습니다 (객체를 앞으로 이동)
Ilya Ivanov

16

이것은 @Reid의 솔루션을 기반으로합니다. 외:

  • 나는 변경하지 않습니다 Array프로토 타입을 .
  • 경계 undefined에서 항목을 오른쪽으로 이동하면 항목이 생성되지 않고 항목이 가장 오른쪽 위치로 이동합니다.

함수:

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

단위 테스트 :

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

게시물 위치를 삽입하면 항목을 삭제 한 이후 색인이 변경됩니다
Yao Zhao

감사합니다. null 요소를 남기지 않고 배열에서 항목을 제거하고 싶었습니다 (splice (indexToRemove)를 사용할 때 발생했습니다. 메소드를 사용하여 제거하려는 항목을 배열 끝으로 이동 한 다음 pop () 삭제 방법
Luke Schoen

"내 항목에 가장 오른쪽 위치로 항목 이동"기능이 마음에 들었습니다. thx
bFunc

11

선택적 매개 변수 가있는 내 라이너 ES6 솔루션on있습니다.

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

제안한 첫 번째 솔루션의 적응 digiguru

매개 변수 onfrom이동하려는 요소의 수입니다 .


해결책은 괜찮습니다. 그러나 프로토 타입을 확장 할 때는 화살표 함수를 사용하지 않아야합니다.이 경우 'this'는 배열 인스턴스가 아니라 Window 객체입니다.
wawka

7

슬라이스 방법을 사용하여 원하는 순서대로 조각으로 새 배열을 만드는 방법이 있습니다.

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)은 [ 'a']
  • arr.slice (2,4)는 당신에게 [ 'b', 'c']
  • arr.slice (4)는 당신에게 [ 'e']를줍니다

1
arr2연결 작업으로 인해 결국 문자열이 된다는 것을 알고 있습니까? :) 그것은 끝납니다 "adc,de".
Ken Franqueiro

6

도움 spliceArray될 수 있는 방법 : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

어레이를 적극적으로 재 인덱싱해야하므로 상대적으로 비쌀 수 있습니다.


그러나 스플 라이스를 수행하자마자 배열 인덱스가 업데이트되어 방금 제거한 요소를 배치 할 위치를 파악하기가 어렵습니다. 특히 양방향으로 움직임을 처리 할 수있는 기능이 필요하기 때문에.
Mark Brown

@ Mark : 문자열을 연결하지 않고 동일한 변수에 저장하고 새 문자열을 만들고 연결하십시오. 아래 답변을 참조하십시오.
Jared Updike

6

기본 미적분을 구현하고 배열 요소를 한 위치에서 다른 위치로 이동하기위한 범용 함수를 만들 수 있습니다.

JavaScript의 경우 다음과 같습니다.

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; 

}

자세한 설명은 "gloommatter"에서 "이동 배열 요소"를 확인하십시오.

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


1
이것은 새로운 배열을 할당하지 않으므로 정답이어야합니다. 감사!
Cᴏʀʏ

링크가 끊어졌습니다.
Rokit

6

여기에 대한 답변을 ECMAScript 6기반 으로 불변의 솔루션을 구현했습니다 @Merc.

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

변수 이름은 짧아 질 수 있으며, 긴 이름을 사용하여 코드 자체를 설명 할 수 있습니다.


확실히 더 나은 대답, 돌연변이는 부작용을 만듭니다
Matt Lo

1
호기심에서, arrayif if를 즉시 반환 fromIndex === toIndex하고 newArray그렇지 않은 경우 에만 if를 작성 하지 않는 이유 는 무엇입니까? 불변성이 변경이없는 경우에도 함수 호출 당 하나의 새로운 사본을 작성해야한다는 의미는 아닙니다. b / c에게이 기능의 길이가 증가한 동기 (스플 라이스 기반 원 라이너에 비해)를 요구하는 것은 성능이며 사용법에 따라 fromIndex종종 동일 할 수 toIndex있습니다.
Robert Monfera

5

불변의 이동 방법 (원래 배열을 변경하지 않은 방법)이 필요했기 때문에 @Reid의 허용 된 대답을 간단하게 Object.assign을 사용하여 스플 라이스를 수행하기 전에 배열의 복사본을 만들도록 수정했습니다.

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

여기에 작동중인 jsfiddle이 있습니다 .


ppl이 돌연변이를 고려하는 것을 항상 보는 것이 좋습니다.
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


2

작고 먼 거리를 이동할 때 두 가지를 결합하여 조금 더 잘 작동했습니다. 나는 상당히 일관된 결과를 얻지 만, 아마도 다른 사람과는 다른 크기로 다르게 작동하도록 똑똑한 사람이 약간 조정할 수 있습니다.

거리를 좁힐 때 다른 방법을 사용하면 스플 라이스를 사용하는 것보다 훨씬 빠릅니다 (x10). 배열 길이에 따라 변경 될 수 있지만 큰 배열의 경우에는 해당됩니다.

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


2

Array 프로토 타입을 사용하여 플레이하는 것은 많은 장소에서 ( Custom .

    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

희망은 누구에게나 유용 할 수 있습니다


2

이 버전은 모든 목적에 이상적이지는 않으며 모든 사람이 쉼표 표현식을 좋아하지는 않지만 순수한 표현을 가진 새로운 라이너를 만들어 새로운 사본을 만듭니다.

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

약간의 성능 향상 버전은 이동이 필요하지 않은 경우 입력 배열을 반환하며, 배열이 변경되지 않으므로 여전히 불변의 사용이 가능하며 순수한 표현입니다.

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

둘 중 하나의 호출은

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

즉, 새로운 사본을 생성하기 위해 확산에 의존합니다. 고정 된 arity 3 move을 사용하면 단일 표현식 속성이나 비파괴적인 특성 또는의 성능 이점이 위태로워집니다 splice. 다시 말하지만, 이는 프로덕션 사용에 대한 제안보다 몇 가지 기준을 충족시키는 예입니다.


1

Array.move.js

요약

배열 내에서 요소를 이동하여 이동 된 요소가 포함 된 배열을 반환합니다.

통사론

array.move(index, howMany, toIndex);

매개 변수

index : 요소를 이동할 인덱스입니다. 음수이면 색인 이 끝에서 시작됩니다.

howMany : 인덱스 에서 이동할 요소 수 .

toIndex : 이동 한 요소를 배치 할 배열의 인덱스입니다. 음수 인 경우 toIndex 가 끝부터 시작됩니다.

용법

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

2
그동안 .move좋아 보이는 (내가 그것을 테스트하지 않았습니다) 작동합니다, 당신은 표준의 일부가 떨어져 있음을 알아 두셔야합니다. polyfill / monkeypatched 함수는 열거 가능한 모든 것이 자신의 것으로 가정하는 일부 코드를 손상시킬 수 있다고 경고하는 것이 좋습니다.
Jeremy J Starcher

1
a = [ "a", "b", "c"]; a.move (0,1,1); // a = [ "a", "b", "c"]는 [ "b", "a", "c"] 여야합니다
Leonard Pauli

2
이 기능은 더 이상 사용되지 않으며 더 이상 지원되지 않을 수 있습니다. 조심하십시오 : developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mostafa

1

@Reid 의 좋은 대답을 사용 했지만 배열의 끝에서 한 단계 더 나아가 요소를 루프로 시작하는 데 어려움을 겪었습니다. . 예를 들어 [ 'a', 'b', 'c']는 .move (2,3)를 호출하여 [ 'c', 'a', 'b']가되어야합니다.

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

1

Reid의 탁월한 답변에 덧붙여서 (그리고 내가 말할 수 없기 때문에); 모듈러스를 사용하여 음수 지수와 너무 큰 지수를 "롤오버"할 수 있습니다.

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


예-음수 인덱스가 지원되므로 정의되지 않은 값을 삽입하는 대신 너무 큰 인덱스를 래핑하는 것이 합리적입니다.
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)


1

나는 이것이 스왑 문제라고 생각했지만 그렇지 않습니다. 여기에 하나의 라이너 솔루션이 있습니다.

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

다음은 작은 테스트입니다.

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' ]

문제는 아이템 교환에 관한 것이 아닙니다. 저자는 삽입 전략에 대한 솔루션을 요청했습니다.
Andreas Dolk

당면한 문제와 관련하여 이것은 객관적으로 잘못된 대답입니다.
벤 스튜어드

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

결과:

["b", "a", "c", "d"]

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


1
SO에 오신 것을 환영합니다! 추가 답변이 21 개 있습니다. 코드 만 배치하지 마십시오. 답의 이점을 설명하십시오.
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);


0

배열 사본이없는 변경 불가능한 버전 :

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);
  }, []);
};

0

가장 좋은 방법은 배열의 새로운 속성을 정의하는 것입니다.

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]

0

돌연변이가없는 ES6 배열 확산 연산자를 사용하는 또 다른 순수한 JS 변형

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


0

이 방법은 원래 배열을 유지하고 경계 오류를 확인합니다.

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}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.