Di chuyển một phần tử mảng từ vị trí mảng này sang vị trí khác


522

Tôi đang có một thời gian khó khăn để tìm ra cách di chuyển một phần tử mảng. Ví dụ, được đưa ra như sau:

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

Làm thế nào tôi có thể viết một chức năng để di chuyển 'd'trước 'b'?

Hay 'a'sau 'c'?

Sau khi di chuyển, các chỉ số của phần tử còn lại sẽ được cập nhật. Điều này có nghĩa là trong ví dụ đầu tiên sau khi di chuyển mảng [0] would = 'a', Array [1] = 'd' Array [2] = 'b', Array [3] = 'c', Array [4] = 'e'

Điều này có vẻ như nó khá đơn giản, nhưng tôi không thể quấn đầu xung quanh nó.


3
Vâng câu hỏi này cũ nhưng vàng
Jalal

sử dụng ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa

Điều đó chỉ hoán đổi các yếu tố tại inittarget.
Matt F.

Câu trả lời:


671

Nếu bạn thích một phiên bản trên npm, di chuyển mảng là gần nhất với câu trả lời này, mặc dù đó không phải là cùng một cách thực hiện. Xem phần sử dụng của nó để biết thêm chi tiết. Phiên bản trước của câu trả lời này (đã sửa đổi Array.prototype.move) trên npm tại Array.prototype.move .


Tôi đã khá thành công với chức năng này:

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

Lưu ý rằng cuối cùng returnchỉ đơn giản là cho mục đích thử nghiệm: splicethực hiện các hoạt động trên mảng tại chỗ, do đó không cần phải trả lại. Bằng cách mở rộng, đây movelà một hoạt động tại chỗ. Nếu bạn muốn tránh điều đó và trả lại một bản sao, hãy sử dụng slice.

Bước qua mã:

  1. Nếu new_indexlớn hơn độ dài của mảng, chúng tôi muốn (tôi đoán) sẽ đệm đúng mảng với undefineds mới . Đoạn mã nhỏ này xử lý việc này bằng cách ấn undefinedvào mảng cho đến khi chúng ta có độ dài phù hợp.
  2. Sau đó, trong arr.splice(old_index, 1)[0], chúng tôi tách ra các yếu tố cũ. splicetrả về phần tử đã được tách ra, nhưng nó nằm trong một mảng. Trong ví dụ trên của chúng tôi, đây là [1]. Vì vậy, chúng tôi lấy chỉ mục đầu tiên của mảng đó để lấy nguyên liệu 1ở đó.
  3. Sau đó, chúng tôi sử dụng spliceđể chèn phần tử này vào vị trí của new_index. Vì chúng tôi đã đệm mảng ở trên nếu new_index > arr.length, nó có thể sẽ xuất hiện ở đúng nơi, trừ khi họ đã làm một cái gì đó kỳ lạ như vượt qua trong một số âm.

Một phiên bản fancier để giải thích cho các chỉ số tiêu cực:

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

Mà nên tính đến những thứ như array_move([1, 2, 3], -1, -2)đúng (di chuyển phần tử cuối cùng đến vị trí thứ hai đến vị trí cuối cùng). Kết quả cho điều đó nên được [1, 3, 2].

Dù bằng cách nào, trong câu hỏi ban đầu của bạn, bạn sẽ làm array_move(arr, 0, 2)cho asau c. Đối với dtrước đây b, bạn sẽ làm array_move(arr, 3, 1).


19
Điều này hoạt động hoàn hảo! Và lời giải thích của bạn rất rõ ràng. Cảm ơn đã dành thời gian để viết này lên.
Mark Brown

16
Bạn không nên thao tác các nguyên mẫu Object và Array, nó gây ra vấn đề khi lặp lại các phần tử.
burak emre

9
@burakemre: Tôi nghĩ rằng kết luận đó không đạt được rõ ràng. Hầu hết các lập trình viên JS giỏi (và hầu hết các thư viện phổ biến) sẽ sử dụng .hasOwnPropertykiểm tra khi lặp lại với những thứ như for..in, đặc biệt là với các thư viện như Prototype và MooTools sửa đổi các nguyên mẫu. Dù sao, tôi đã không cảm thấy đó là một vấn đề đặc biệt quan trọng trong một ví dụ tương đối hạn chế như thế này và có một sự chia rẽ tốt đẹp trong cộng đồng về việc sửa đổi nguyên mẫu có phải là một ý tưởng tốt hay không. Thông thường, các vấn đề lặp là mối quan tâm ít nhất, mặc dù.
Reid

3
Không cần vòng lặp trong bước 1, bạn chỉ cần sử dụng this[new_index] = undefined;trong ifkhối. Vì các mảng Javascript rất thưa thớt, điều này sẽ mở rộng kích thước mảng để bao gồm new_index .spliceđể hoạt động nhưng không cần tạo bất kỳ yếu tố can thiệp nào.
Michael

3
@Michael: Điểm tốt - nhưng this[new_index] = undefinedthực sự sẽ đặt một vị trí undefinedtrong mảng trước chỉ mục chính xác. (Ví dụ: [1,2,3].move(0,10)sẽ có 1trong khe 10 và undefinedtrong khe 9.) Thay vào đó, nếu độ thưa là OK, chúng ta có thể thực hiện this[new_index] = this.splice(old_index, 1)[0]mà không cần lệnh gọi mối nối khác (thay vào đó hãy gọi là if / other).
Reid

268

Đây là một lớp lót tôi tìm thấy trên JSPerf ....

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

Thật tuyệt khi đọc, nhưng nếu bạn muốn hiệu suất (trong các tập dữ liệu nhỏ) hãy thử ...

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

Tôi không thể nhận được bất kỳ tín dụng nào, tất cả nên đến Richard Scarrott . Nó đánh bại phương thức dựa trên mối nối cho các tập dữ liệu nhỏ hơn trong bài kiểm tra hiệu năng này . Tuy nhiên, nó chậm hơn đáng kể trên các tập dữ liệu lớn hơn như Darwayne chỉ ra .


2
Giải pháp hiệu quả hơn của bạn là chậm hơn trên các bộ dữ liệu lớn. jsperf.com/array-prototype-move/8
Darwayne

44
Điều này có vẻ như một sự đánh đổi thực sự ngớ ngẩn. Hiệu suất trên các tập dữ liệu nhỏ là một mức tăng không đáng kể, nhưng mất trên các tập dữ liệu lớn là một mất mát đáng kể. Trao đổi ròng của bạn là tiêu cực.
Kyeotic

3
@Reid Đó không phải là một yêu cầu. IMO có thể giả định rằng độ dài của mảng không được sửa đổi.
robsch

3
Giải pháp một dòng cần xử lý hai tình huống:from >= to ? this.splice(to, 0, this.splice(from, 1)[0]) : this.splice(to - 1, 0, this.splice(from, 1)[0]);
Rob L

13
Xin đừng bao giờ sửa đổi các nguyên mẫu dựng sẵn, bao giờ hết. nczonline.net/blog/2010/03/02/ Từ
LJHarb

230

Tôi thích cách này. Nó súc tích và nó hoạt động.

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

Lưu ý: luôn nhớ kiểm tra giới hạn mảng của bạn.

Chạy đoạn trích trên jsFiddle


29
Vì Array.splice trả về (các) giá trị bị loại bỏ trong một Mảng mới, nên bạn có thể viết nó dưới dạng một lớp lót ... Array.splice (index + 1, 0, Array.splice (index, 1) [0]);
Eric

49
Cá nhân tôi thích mã 3 dòng. Dễ hiểu hơn: Lấy một bản sao của phần tử; loại bỏ nó khỏi mảng; chèn nó vào một vị trí mới. Lớp lót ngắn hơn nhưng không quá rõ ràng để người khác hiểu ...
Philipp

2
Mã ngắn và đơn giản. Nhưng đó là năm 2019 !!, Tạo một bản sao của mảng và trả lại thay vì thay đổi mảng. Điều này sẽ làm cho chức năng của bạn "mảng" tuân thủ các tiêu chuẩn lập trình chức năng
SamwellTarly

4
OK nhưng không phải tất cả mọi thứ không phải cũng không phải là chức năng phù hợp chương trình; cộng với điều này vẫn có thể hữu ích trong lập trình chức năng bên trong một thủ tục để thao tác các mảng cục bộ.
SteakOverflow

36

Phương thức splice () thêm / xóa các mục vào / từ một mảng và trả về (các) mục đã xóa .

Lưu ý: Phương pháp này thay đổi mảng ban đầu. / 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"]

vì chức năng này là chuỗi, điều này cũng hoạt động:

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

demo tại đây


Có thư viện nào sử dụng cái này không? Khá gọn gàng!
uicoding

Xem các bình luận khác về điều này: đó là một ý tưởng tồi để sửa đổi các nguyên mẫu tích hợp như Array và Object. Bạn sẽ phá vỡ mọi thứ.
địa lý

27

2c của tôi. Dễ đọc, nó hoạt động, nhanh, không tạo mảng mới.

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
Ở chuỗi đầu tiên của hàm, bạn nên trả về array, vì nó được thực hiện ở cuối.
Sergey Voronezhskiy

3
Đúng là làm thế nào tôi bỏ lỡ điều đó? Đã sửa!
Merc

Tôi thích giải pháp đơn giản và linh hoạt nhất của bạn. Cám ơn!
Roman M. Koss

18

Có ý tưởng này từ @Reid về việc đẩy một cái gì đó vào vị trí của vật phẩm được cho là sẽ được di chuyển để giữ cho kích thước mảng không đổi. Điều đó không đơn giản hóa các tính toán. Ngoài ra, đẩy một đối tượng trống có thêm lợi ích là có thể tìm kiếm nó một cách duy nhất sau này. Điều này hoạt động vì hai đối tượng không bằng nhau cho đến khi chúng được đề cập đến cùng một đối tượng.

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

Vì vậy, đây là hàm có trong mảng nguồn và các chỉ mục đích, nguồn. Bạn có thể thêm nó vào Array.prototype nếu cần.

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
Điều này có vẻ đầy hứa hẹn ... và tôi không biết rằng về so sánh js javascript. Cảm ơn!
Mark Brown

Không hoạt động cho trường hợp sourceIndex = 0,destIndex = 1
Sergey Voronezhskiy

destIndexcó nghĩa là chỉ mục trước khi phần tử nguồn được di chuyển trong mảng.
Anurag

Đây là câu trả lời tốt nhất cho đến nay. Các câu trả lời khác đã thất bại một vài bài kiểm tra đơn vị trong bộ của tôi (di chuyển đối tượng về phía trước)
Ilya Ivanov

16

Điều này dựa trên giải pháp của @ Reid. Ngoại trừ:

  • Tôi không thay đổi Arraynguyên mẫu.
  • Di chuyển một mục ra khỏi giới hạn bên phải không tạo ra undefinedcác mục, nó chỉ di chuyển mục đó sang vị trí bên phải nhất.

Chức năng:

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

Bài kiểm tra đơn vị:

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

Điều này là sai, nếu bạn chèn một vị trí bài đăng, chỉ mục sẽ thay đổi kể từ khi bạn xóa mục
Yao Zhao

Cảm ơn bạn. Tôi muốn xóa một mục khỏi một mảng mà không để lại phần tử null (xảy ra khi sử dụng splice (indexToRemove). Tôi đã sử dụng phương thức của bạn để di chuyển mục mà tôi muốn xóa đến cuối mảng và sau đó sử dụng pop () phương pháp để loại bỏ.
Luke Schoen

thích tính năng "di chuyển vật phẩm sang vị trí ngoài cùng bên phải", hữu ích cho trường hợp của tôi. thx
bFunc

11

Đây là một giải pháp ES6 lót của tôi với một tham số tùy chọn on.

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

Thích ứng của giải pháp đầu tiên được đề xuất bởi digiguru

Tham số onlà số phần tử bắt đầu từ frombạn muốn di chuyển.


Giải pháp là tốt. Tuy nhiên, khi bạn mở rộng một nguyên mẫu, bạn không nên sử dụng chức năng mũi tên vì trong trường hợp này, 'đây' không phải là một thể hiện mảng mà là đối tượng Window.
wawka

7

Một cách tiếp cận sẽ là tạo một mảng mới với các phần theo thứ tự bạn muốn, sử dụng phương thức lát.

Thí dụ

var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
  • Array.slice (0,1) cung cấp cho bạn ['a']
  • mảng.slice (2,4) cung cấp cho bạn ['b', 'c']
  • mảng.slice (4) cung cấp cho bạn ['e']

1
Bạn có nhận ra rằng arr2kết thúc của bạn là một chuỗi do các hoạt động nối, phải không? :) Nó kết thúc là "adc,de".
Ken Franqueiro

6

Các splicephương pháp Arraycó thể giúp: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

Chỉ cần lưu ý rằng nó có thể tương đối đắt tiền vì nó phải chủ động lập chỉ mục lại mảng.


Đúng, nhưng ngay khi tôi thực hiện mối nối, các chỉ số mảng được cập nhật, điều này khiến tôi gặp khó khăn trong việc tìm ra nơi đặt phần tử mà tôi vừa loại bỏ. Đặc biệt là vì tôi cần chức năng để có thể xử lý di chuyển theo cả hai hướng.
Đánh dấu Brown

@Mark: không ghép chuỗi và lưu nó vào cùng một biến, tạo một chuỗi mới và nối chuỗi đó. Xem câu trả lời của tôi dưới đây.
Jared Updike

6

Bạn có thể thực hiện một số Tính toán cơ bản và tạo một hàm phổ quát để di chuyển phần tử mảng từ vị trí này sang vị trí khác.

Đối với JavaScript, nó trông như thế này:

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; 

}

Kiểm tra "các phần tử mảng di chuyển" tại "gloommatter" để được giải thích chi tiết.

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


1
Điều này nên là câu trả lời chính xác, vì nó không phân bổ bất kỳ mảng mới. Cảm ơn!
Cᴏʀʏ

Liên kết bị hỏng.
Rokit

6

Tôi đã thực hiện một ECMAScript 6giải pháp bất biến dựa trên @Merccâu trả lời ở đây:

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

Tên biến có thể được rút ngắn, chỉ cần sử dụng tên dài để mã có thể tự giải thích.


chắc chắn là một câu trả lời tốt hơn, đột biến tạo ra tác dụng phụ
Matt Lo

1
Vì tò mò, tại sao không quay lại arrayngay lập tức nếu fromIndex === toIndexvà chỉ tạo ra newArraynếu không phải vậy? Tính không thay đổi không có nghĩa là một bản sao mới phải được tạo cho mỗi lệnh gọi hàm ngay cả khi không có thay đổi. Chỉ cần hỏi b / c động cơ tăng chiều dài của chức năng này (liên quan đến một lớp lót dựa trên mối nối) là hiệu suất và fromIndexthường có thể bằng nhau toIndex, tùy thuộc vào cách sử dụng.
Robert Monfera

5

Tôi cần một phương thức di chuyển bất biến (một phương pháp không thay đổi mảng ban đầu), vì vậy tôi đã điều chỉnh câu trả lời được chấp nhận của @ Reid để đơn giản sử dụng Object.assign để tạo một bản sao của mảng trước khi thực hiện ghép nối.

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

Đây là một jsfiddle cho thấy nó trong hành động .


Thật tốt khi thấy ppl đưa các đột biến vào xem xét.
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

Cuối cùng tôi đã kết hợp hai trong số này để làm việc tốt hơn một chút khi di chuyển cả khoảng cách nhỏ và lớn. Tôi nhận được kết quả khá nhất quán, nhưng điều này có thể được điều chỉnh một chút bởi một người thông minh hơn tôi để làm việc khác nhau cho các kích cỡ khác nhau, v.v.

Sử dụng một số phương pháp khác khi di chuyển các đối tượng khoảng cách nhỏ nhanh hơn đáng kể (x10) so với sử dụng mối nối. Điều này có thể thay đổi tùy thuộc vào độ dài của mảng, nhưng nó đúng với các mảng lớn.

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

Nó được nêu ở nhiều nơi ( thêm các chức năng tùy chỉnh vào Array.prototype ) chơi với nguyên mẫu Array có thể là một ý tưởng tồi, dù sao tôi đã kết hợp tốt nhất từ ​​các bài đăng khác nhau, tôi đã sử dụng Javascript hiện đại này:

    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

Hy vọng có thể hữu ích cho bất cứ ai


2

Phiên bản này không lý tưởng cho tất cả các mục đích và không phải ai cũng thích biểu thức dấu phẩy, nhưng đây là một biểu đồ đơn thuần là biểu thức thuần túy, tạo ra một bản sao mới:

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

Một phiên bản được cải thiện hiệu suất một chút sẽ trả về mảng đầu vào nếu không cần di chuyển, vẫn không sao cho việc sử dụng bất biến, vì mảng sẽ không thay đổi và đó vẫn là một biểu thức thuần túy:

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

Yêu cầu của một trong hai là

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

tức là nó dựa vào việc lan truyền để tạo ra một bản sao mới. Sử dụng arity 3 cố định movesẽ gây nguy hiểm cho thuộc tính biểu thức đơn hoặc tính chất không phá hủy hoặc lợi ích hiệu suất của splice. Một lần nữa, đó là một ví dụ đáp ứng một số tiêu chí hơn là một gợi ý cho việc sử dụng sản xuất.


1

Array.move.js

Tóm lược

Di chuyển các phần tử trong một mảng, trả về một mảng chứa các phần tử được di chuyển.

Cú pháp

array.move(index, howMany, toIndex);

Thông số

index : Chỉ mục để di chuyển các phần tử. Nếu tiêu cực, chỉ số sẽ bắt đầu từ cuối.

howMany : Số phần tử để di chuyển từ chỉ mục .

to Index: Chỉ mục của mảng để đặt các phần tử được di chuyển. Nếu âm, to Index sẽ bắt đầu từ cuối.

Sử dụng

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

2
Mặc dù có .movevẻ như nó hoạt động (tôi chưa thử nghiệm), bạn nên lưu ý rằng nó không phải là một phần của bất kỳ tiêu chuẩn nào. Cũng rất tốt để cảnh báo mọi người rằng các hàm polyfill / khỉ có thể phá vỡ một số mã giả định mọi thứ có thể đếm được là của họ.
Jeremy J Starcher

1
a = ["a", "b", "c"]; a.move (0,1,1); // a = ["a", "b", "c"], nên là ["b", "a", "c"]
Leonard Pauli

2
Tính năng này đã lỗi thời và có thể không được hỗ trợ nữa. Hãy cẩn thận Xem: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mẹo

1

Tôi đã sử dụng câu trả lời hay của @Reid , nhưng vật lộn với việc di chuyển một phần tử từ cuối mảng một bước xa hơn - đến đầu (như trong một vòng lặp ). Ví dụ: ['a', 'b', 'c'] sẽ trở thành ['c', 'a', 'b'] bằng cách gọi .move (2,3)

Tôi đã đạt được điều này bằng cách thay đổi trường hợp cho 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

Là một bổ sung cho câu trả lời tuyệt vời của Reid (và vì tôi không thể bình luận); Bạn có thể sử dụng modulo để làm cho cả hai chỉ số tiêu cực và các chỉ số quá lớn "cuộn qua":

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


Có - vì theo tôi, các chỉ số tiêu cực được hỗ trợ, có vẻ hợp lý để bọc các chỉ số quá lớn thay vì chèn các giá trị không xác định, theo ý kiến ​​của tôi.
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

Tôi nghĩ rằng đây là một vấn đề trao đổi nhưng nó không phải là. Đây là giải pháp một lớp lót của tôi:

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

Đây là một thử nghiệm nhỏ:

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

Chà, câu hỏi không phải là về việc trao đổi vật phẩm. Tác giả đã yêu cầu một giải pháp cho một chiến lược chèn.
Andreas Dolk

Đối với câu hỏi trong tầm tay, đây là câu trả lời khách quan.
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 );

kết quả:

["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
Chào mừng đến với SO! Có 21 câu trả lời bổ sung ... vì vậy, xin vui lòng, chỉ cần đặt mã. Giải thích lợi ích của câu trả lời của bạn.
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

Phiên bản không thay đổi mà không cần sao chép mảng:

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

Tôi nghĩ rằng cách tốt nhất là xác định một thuộc tính mới cho Mảng

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

Một biến thể JS thuần túy khác sử dụng toán tử trải rộng mảng ES6 không có đột biế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))
      
 


0

Phương pháp này sẽ bảo toàn mảng ban đầu và kiểm tra các lỗi giới hạn.

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}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.