配列要素をある配列位置から別の配列位置に移動する


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] = 「e」

これはかなりシンプルなように思えますが、頭を包むことはできません。


3
Jalal

ES6を使用してconst changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa

それはちょうどで要素を交換initしてtarget
マット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そこにrawを取得します。
  3. 次にsplice、この要素をnew_indexの場所に挿入するために使用します。ifの上に配列をパディングしたので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)適切に説明する必要があります(最後の要素を最後から2番目に移動します)。その結果はになるはずです[1, 3, 2]

いずれにせよ、元の質問ではarray_move(arr, 0, 2)a後のためにしcます。以下のためにd前にb、あなたが行うだろうarray_move(arr, 3, 1)


19
これは完璧に動作します!そして、あなたの説明は非常に明確です。これを書いてくれてありがとう。
Mark Brown、

16
オブジェクトと配列のプロトタイプは操作しないでください。要素を反復するときに問題が発生します。
burak emre 2013年

9
@burakemre:結論はそれほど明確に達していないと思います。ほとんどの優れたJSプログラマー(および最も人気のあるライブラリー)は、.hasOwnPropertyfor..inのようなもの、特にプロトタイプを変更するPrototypeやMooToolsなどのライブラリーを反復するときにチェックを使用します。とにかく、このような比較的限定された例では特に重要な問題だとは感じませんでした。プロトタイプの変更が適切かどうかについては、コミュニティでうまく分かれています。ただし、通常、反復の問題は最も心配されません。
Reid

3
手順1のループは必要ありません。ブロックthis[new_index] = undefined;内で使用できifます。JavaScript配列はスパースなので、これは配列サイズを拡張してnew_indexを含め、が機能.spliceするようにしますが、間に要素を作成する必要はありません。
マイケル

3
@マイケル:良い点-しかし、this[new_index] = undefined実際には、正しいインデックスの前にundefined配列スロットにを配置します。(例えば、[1,2,3].move(0,10)必要があります1スロット10にし、undefinedスパースがOKであれば、私たちが行うことができ、むしろスロット9に)this[new_index] = this.splice(old_index, 1)[0](それがある場合/他の代わりにする)他のスプライスコールなし。
リード

268

これが、私がJSPerfで見つけた1つのライナーです。

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

私は信用できません、それはすべてリチャード・スカロットに行くべきです。このパフォーマンステストでは、小さいデータセットのスプライスベースの方法よりも優れています。ただし、ダーウェインが指摘するように、大規模なデータセットでは大幅に遅くなります。


2
大規模なデータセットでは、パフォーマンスの高いソリューションは遅くなります。jsperf.com/array-prototype-move/8
Darwayne

44
これは本当にばかげたトレードオフのようです。小さなデータセットでのパフォーマンスはごくわずかですが、大きなデータセットでの損失は大きな損失です。あなたの純交換はマイナスです。
Kyeotic 2013年

3
@Reidそれは要件ではありませんでした。IMO配列の長さが変更されないと仮定しても問題ありません。
robsch 2016年

3
二つの状況に対処するための一つのラインソリューションの必要性:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
ロブ・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は削除された値を新しい配列で返すため、1つのライナーとして書き込むことができます... arr.splice(index + 1、0、arr.splice(index、1)[0]);
エリック

49
個人的に私は3行のコードを好みます。簡単に理解できます。要素のコピーを取得します。配列から削除します。新しい位置に挿入します。1つのライナーは短いですが、他の人が理解できるほど明確ではありません...
Philipp

2
短くてシンプルなコード。しかし、それは2019年です!!配列のクローンを作成し、配列を変更する代わりにそれを返します。これにより、関数「arraymove」は関数型プログラミング標準に準拠します
SamwellTarly

4
大丈夫ですが、すべて関数型プログラミングに準拠している必要はありません。さらに、これはローカル配列を操作するためのプロシージャ内の関数型プログラミングにも役立ちます。
SteakOverflow

36

splice()メソッドは、配列にアイテムを追加/削除し、削除されたアイテムを返します。

注:このメソッドは、元の配列を変更します。/ 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(','));

ここでデモ


これを使用するライブラリはありますか?かなりすっきり!
16年

これに関する他のコメントを参照してください。配列やオブジェクトなどの組み込みのプロトタイプを変更することは悪い考えです。あなたは物事を壊すでしょう。
ジオイデシック2018年

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

私はあなたのシンプルで柔軟なソリューションが一番好きです。THX!
ローマンM.コス2017年

18

配列サイズを一定に保つために移動されるはずのアイテムの代わりに何かをプッシュするという@Reidからこのアイデアを得ました。それは計算を単純化します。また、空のオブジェクトをプッシュすると、後で一意に検索できるという利点もあります。これは、2つのオブジェクトが同じオブジェクトを参照するまで等しくないためです。

({}) == ({}); // 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
これは有望に見えます...そして私はjavascript js比較についてそれを知りませんでした。ありがとう!
Mark Brown、

Does'tはケースのために働くsourceIndex = 0destIndex = 1
セルゲイVoronezhskiy

destIndexソース要素が配列内で移動される前のインデックスであることを意味します。
Anurag 2017

これが今のところ最良の答えです。他の回答は私のスイートでいくつかの単体テストに失敗しました(オブジェクトを前方に移動)
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()を使用した削除するメソッド
ルーク・シェーン

「アイテムを右端に移動する」機能が好きで、私の場合に便利です。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

パラメータonは、from移動したい要素から始まる要素の数です。


解決策は結構です。ただし、プロトタイプを展開するときは、矢印関数を使用しないでください。この場合、「this」は配列インスタンスではなく、たとえばWindowオブジェクトです。
wawka

7

1つのアプローチは、スライスメソッドを使用して、必要な順序でピースを含む新しい配列を作成することです。

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、2011年

6

splice方法Arrayが役立つかもしれません:https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

配列のインデックスを積極的に再作成する必要があるため、比較的高価になる可能性があることに注意してください。


そうですが、スプライスを実行するとすぐに配列のインデックスが更新されるため、削除したばかりの要素をどこに配置するのかわかりません。特に両方向の動きに対応できる機能が必要なので。
Mark Brown

@Mark:文字列をスプライスして同じ変数に保存せず、新しい文字列を作成してそれをスプライスします。以下の私の答えを参照してください。
Jared Updike、2011年

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」の「moving array elements」をご覧ください。

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 だけをすぐに返さず、そうでない場合 fromIndex === toIndexにのみ作成するのはなぜnewArrayですか?不変性は、変更がない場合でも、関数呼び出しごとに1つの新しいコピーを作成する必要があることを意味しません。(スプライスベースの1ライナーと比較して)この関数の長さを長くする動機をb / cに尋ねるだけでパフォーマンスfromIndexが得toIndexられ、使用方法によっては、と等しい場合がよくあります。
Robert Monfera

5

不変のmoveメソッド(元の配列を変更しないもの)が必要だったので、@ 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アスカリ2017

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

近距離と遠距離の両方を移動するときに、これらの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.prototypeにカスタム関数を追加する)Arrayプロトタイプで遊ぶのは悪い考えかもしれません、とにかく私は様々な投稿から最高のものを組み合わせました、私はこれを現代の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

誰にでも役立つことを願っています


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)

つまり、新しいコピーを生成するために拡散に依存しています。固定アリティ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関数は、列挙可能なものはすべて自分のものであると想定する一部のコードを壊す可能性があることを人々に警告するのも良いことです。
ジェレミーJスターチャー

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/...
モスタファ

1

は@Reidの良い答えを使用しましたが、要素を配列の最後から1ステップ先に(ループのように)最初に移動するのに苦労しました。例:['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

リードの優れた答えへの追加として(そして私がコメントできないため); モジュロを使用して、負のインデックスと大きすぎるインデックスの両方を「ロールオーバー」することができます。

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の追加の回答があります...したがって、コードを配置するだけではいけません。答えの利点を説明してください。
DavidGarcíaBodego

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.