ย้ายองค์ประกอบอาเรย์จากตำแหน่งอาเรย์หนึ่งไปยังอีกตำแหน่งหนึ่ง


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

ใช้ ES6const changeValuePosition = (arr, init, target) => {[arr[init],arr[target]] = [arr[target],arr[init]]; return arr}
muhsalaa

ที่เพิ่งแลกเปลี่ยนองค์ประกอบที่และinit target
Matt F.

คำตอบ:


671

หากคุณต้องการรุ่นในเวลา 19.00 น. การย้ายอาร์เรย์เป็นคำตอบที่ใกล้เคียงที่สุดแม้ว่าจะไม่ใช่การติดตั้งแบบเดียวกันก็ตาม ดูส่วนการใช้งานสำหรับรายละเอียดเพิ่มเติม รุ่นก่อนหน้าของคำตอบนี้ (ที่มีการปรับเปลี่ยน 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มากกว่าความยาวของอาเรย์เราต้องการ (ฉันเข้าใจ) เพื่อแพ็ดอาเรย์อย่างถูกต้องด้วยundefineds ใหม่ ตัวอย่างเล็ก ๆ นี้จัดการสิ่งนี้โดยการกด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)สำหรับหลังa cสำหรับdก่อนที่คุณจะทำbarray_move(arr, 3, 1)


19
มันทำงานได้อย่างสมบูรณ์แบบ! และคำอธิบายของคุณชัดเจนมาก ขอบคุณที่สละเวลาเขียนสิ่งนี้
มาร์คบราวน์

16
คุณไม่ควรจัดการต้นแบบและวัตถุในอาร์เรย์มันทำให้เกิดปัญหาเมื่อวนซ้ำองค์ประกอบ
burak emre

9
@ Burakemre: ฉันคิดว่าข้อสรุปนั้นไม่ถึงอย่างชัดเจน โปรแกรมเมอร์ JS ที่ดีส่วนใหญ่ (และห้องสมุดยอดนิยมส่วนใหญ่) จะใช้การ.hasOwnPropertyตรวจสอบเมื่อวนซ้ำกับสิ่งต่าง ๆ เช่น .. ในโดยเฉพาะอย่างยิ่งกับไลบรารีเช่น Prototype และ MooTools ซึ่งดัดแปลงต้นแบบ อย่างไรก็ตามฉันไม่รู้สึกว่ามันเป็นปัญหาที่สำคัญโดยเฉพาะในตัวอย่างที่ค่อนข้าง จำกัด เช่นนี้และมีการแบ่งที่ดีในชุมชนว่าการดัดแปลงต้นแบบเป็นความคิดที่ดีหรือไม่ โดยทั่วไปปัญหาการทำซ้ำเป็นสิ่งที่น่ากังวลน้อยที่สุด
เรด

3
ไม่จำเป็นต้องใช้ลูปในขั้นตอนที่ 1 คุณสามารถใช้this[new_index] = undefined;ภายในifบล็อก เนื่องจากอาร์เรย์ Javascript มีขนาดเล็กจะเป็นการขยายขนาดอาร์เรย์เพื่อรวม new_index สำหรับการ.spliceทำงาน แต่ไม่จำเป็นต้องสร้างองค์ประกอบใด ๆ
Michael

3
@Michael: จุดดี - แต่การทำthis[new_index] = undefinedจริง ๆ แล้วจะใส่ลงundefinedในสล็อตอาเรย์ก่อนดัชนีที่ถูกต้อง (เช่น[1,2,3].move(0,10)จะมี1ในช่องที่ 10 และundefinedในช่องที่ 9) แต่ถ้าการกระจายข้อมูลเป็นปกติเราสามารถทำได้this[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;
    }
  }

ฉันไม่สามารถใช้บัตรเครดิตใด ๆ ก็ควรไปที่ริชาร์ด Scarrott มันเต้นตามวิธี splice สำหรับชุดข้อมูลขนาดเล็กในการทดสอบประสิทธิภาพนี้ แต่มันเป็นอย่างช้าในชุดข้อมูลที่มีขนาดใหญ่เป็น Darwayne ชี้ให้เห็น


2
โซลูชันที่มีประสิทธิภาพมากขึ้นของคุณจะช้าลงในชุดข้อมูลขนาดใหญ่ jsperf.com/array-prototype-move/8
Darwayne

44
ดูเหมือนว่าการค้าโง่ ๆ ประสิทธิภาพของชุดข้อมูลขนาดเล็กเป็นสิ่งที่ได้มาเพียงเล็กน้อย แต่การสูญเสียในชุดข้อมูลขนาดใหญ่นั้นเป็นการสูญเสียที่สำคัญ การแลกเปลี่ยนสุทธิของคุณเป็นลบ
Kyeotic

3
@ เรดนั่นไม่ใช่ข้อกำหนด 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);
}

หมายเหตุ: อย่าลืมตรวจสอบขอบเขตของอาร์เรย์

เรียกใช้ Snippet บน jsFiddle


29
เนื่องจาก Array.splice ส่งคืนค่าที่ลบออกใน Array ใหม่คุณสามารถเขียนเป็นหนึ่งซับ ... arr.splice (index + 1, 0, arr.splice (index, 1) [0]);
Eric

49
ส่วนตัวฉันชอบรหัส 3 บรรทัด ง่ายต่อการเข้าใจ: รับสำเนาขององค์ประกอบ ลบออกจากอาร์เรย์ ใส่ไว้ในตำแหน่งใหม่ ซับหนึ่งสั้นกว่า แต่ไม่ค่อยชัดเจนนักสำหรับคนอื่น ๆ ที่จะเข้าใจ ...
ฟิลิปป์

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(','));

ตัวอย่างที่นี่


มีห้องสมุดใดบ้างที่ใช้สิ่งนี้? ค่อนข้างเรียบร้อย!
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

ฉันชอบโซลูชันที่เรียบง่ายและยืดหยุ่นของคุณมากที่สุด ขอบคุณ!
Roman M. Koss

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
ดูเหมือนว่าจะมีแนวโน้ม ... และฉันไม่รู้ว่าเกี่ยวกับการเปรียบเทียบ javascript js ขอบคุณ!
มาร์คบราวน์

กวนทำงานสำหรับกรณีsourceIndex = 0,destIndex = 1
Sergey 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

ขอบคุณ. ฉันต้องการที่จะลบรายการออกจากอาร์เรย์โดยไม่ปล่อยให้เป็นโมฆะองค์ประกอบ (ซึ่งเกิดขึ้นเมื่อใช้ splice (indexToRemove) ฉันใช้วิธีการของคุณเพื่อย้ายรายการที่ฉันต้องการลบไปยังจุดสิ้นสุดของอาร์เรย์แล้วใช้ป๊อป () วิธีการลบ
Luke Schoen

ชอบ "ย้ายรายการไปยังตำแหน่งที่เหมาะสมที่สุด" ซึ่งมีประโยชน์สำหรับกรณีของฉัน ขอบคุณ
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คุณต้องการย้าย


วิธีแก้ปัญหาก็ดี อย่างไรก็ตามเมื่อคุณขยายต้นแบบคุณไม่ควรใช้ฟังก์ชั่นลูกศรเพราะในกรณีนี้ 'นี่' ไม่ใช่อินสแตนซ์ของอาร์เรย์ แต่สำหรับวัตถุหน้าต่างตัวอย่าง
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

spliceวิธีการArrayอาจช่วย: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice

เพียงจำไว้ว่ามันอาจจะค่อนข้างแพงเนื่องจากต้องทำการจัดทำดัชนีอาร์เรย์อีกครั้ง


ใช่ แต่ทันทีที่ฉันทำการประกบกันดัชนีอาร์เรย์ได้รับการอัปเดตซึ่งทำให้ฉันยากที่จะทราบว่าจะวางองค์ประกอบที่ฉันเพิ่งลบออกไปได้อย่างไร โดยเฉพาะอย่างยิ่งตั้งแต่ฉันต้องการฟังก์ชั่นเพื่อให้สามารถจัดการกับการเคลื่อนไหวในทั้งสองทิศทาง
มาร์คบราวน์

@ 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
ด้วยความอยากรู้อยากเห็นทำไมไม่เพียงแค่คืนarrayทันทีหาก fromIndex === toIndexและสร้างเฉพาะในnewArrayกรณีที่ไม่ใช่เท่านั้น ความไม่สามารถเปลี่ยนแปลงได้ไม่ได้หมายความว่าจะต้องสร้างสำเนาใหม่หนึ่งรายการต่อการเรียกใช้ฟังก์ชันแม้ว่าจะไม่มีการเปลี่ยนแปลงก็ตาม เพียงแค่ขอให้ 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.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)

เช่นมันอาศัยการแพร่กระจายเพื่อสร้างสำเนาใหม่ ใช้ arity คงที่ 3 จะเป็นอันตรายต่อทั้งคุณสมบัติเดียวแสดงออกหรือธรรมชาติไม่ทำลายหรือผลประโยชน์ของmove spliceอีกครั้งเป็นตัวอย่างที่ตรงกับเกณฑ์มากกว่าข้อเสนอแนะสำหรับการใช้งานจริง


1

Array.move.js

สรุป

ย้ายองค์ประกอบภายในอาร์เรย์ส่งคืนอาร์เรย์ที่มีองค์ประกอบที่ย้าย

วากยสัมพันธ์

array.move(index, howMany, toIndex);

พารามิเตอร์

ดัชนี : ดัชนีที่จะย้ายองค์ประกอบ ถ้าลบให้ทำดัชนีจะเริ่มจากจุดสิ้นสุด

เท่าไหร่ : จำนวนองค์ประกอบที่จะย้ายจากดัชนีดัชนี

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

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
ในขณะที่.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/
......

1

ฉันใช้คำตอบที่ดีของ @Reidแต่ต่อสู้กับการย้ายองค์ประกอบจากจุดจบของอาร์เรย์ไปอีกขั้นหนึ่ง - ไปยังจุดเริ่มต้น (เช่นในลูป ) เช่น ['a', 'b', 'c'] ควรกลายเป็น ['c', 'a', 'b'] โดยการเรียก. moove (2,3)

ฉันได้รับสิ่งนี้โดยการเปลี่ยนเคสสำหรับ 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 = (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

เมื่อคำนึงถึงคำถามในมือนี่เป็นคำตอบที่ผิดอย่างเป็นกลาง
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 );

ผลลัพธ์:

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

ตัวแปร JS บริสุทธิ์อีกตัวหนึ่งที่ใช้ตัวดำเนินการแพร่กระจายอาร์เรย์ ES6 โดยไม่มีการกลายพันธุ์

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.