Xoay các phần tử trong một mảng trong JavaScript


86

Tôi đã tự hỏi đâu là cách hiệu quả nhất để xoay một mảng JavaScript.

Tôi đã đưa ra giải pháp này, trong đó giá trị dương nsẽ xoay mảng sang phải và giá trị âm nsang trái ( -length < n < length):

Array.prototype.rotateRight = function( n ) {
  this.unshift( this.splice( n, this.length ) );
}

Sau đó có thể sử dụng theo cách này:

var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
months.rotate( new Date().getMonth() );

Phiên bản gốc của tôi ở trên có một lỗ hổng, như được chỉ ra bởi Christoph trong các nhận xét dưới đây, một phiên bản chính xác là (trả lại bổ sung cho phép chuỗi):

Array.prototype.rotateRight = function( n ) {
  this.unshift.apply( this, this.splice( n, this.length ) );
  return this;
}

Có giải pháp nào nhỏ gọn hơn và / hoặc nhanh hơn, có thể trong ngữ cảnh của khung JavaScript không? (không có phiên bản đề xuất nào dưới đây nhỏ gọn hơn hoặc nhanh hơn)

Có khung JavaScript nào có tích hợp sẵn xoay mảng không? (Vẫn chưa được ai trả lời)


1
Tôi không hiểu ví dụ của bạn nên làm gì. Tại sao bạn không sử dụng months[new Date().getMonth()]để lấy tên của tháng hiện tại?
Gumbo

1
@Jean: mã bị hỏng: theo cách bạn làm, nó sẽ chuyển các phần tử nối thành một mảng chứ không phải riêng lẻ; bạn sẽ phải sử dụng apply()để làm cho công việc thực hiện của bạn
Christoph

Ngày nay, luân chuyển sẽ sửa đổi tháng để hiển thị danh sách này (với Decở vị trí đầu tiên):["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"]
Jean Vincent

@Christoph, bạn nói đúng, điều này sẽ không hoạt động như một chức năng xoay vòng chung. Nó chỉ hoạt động nếu được sử dụng để chuyển đổi thành một chuỗi ngay sau đó.
Jean Vincent,

@Jean: bạn có thể sửa phiên bản của mình thông qua Array.prototype.unshift.apply(this, this.splice(...))- phiên bản của tôi làm được điều tương tự, nhưng sử dụng push()thay vìunshift()
Christoph

Câu trả lời:


60

Phiên bản chung chung, an toàn về loại làm thay đổi mảng:

Array.prototype.rotate = (function() {
    // save references to array functions to make lookup faster
    var push = Array.prototype.push,
        splice = Array.prototype.splice;

    return function(count) {
        var len = this.length >>> 0, // convert to uint
            count = count >> 0; // convert to int

        // convert count to value in range [0, len)
        count = ((count % len) + len) % len;

        // use splice.call() instead of this.splice() to make function generic
        push.apply(this, splice.call(this, 0, count));
        return this;
    };
})();

Trong phần bình luận, Jean đã nêu vấn đề rằng mã không hỗ trợ quá tải push()splice(). Tôi không nghĩ rằng điều này thực sự hữu ích (xem phần bình luận), nhưng một giải pháp nhanh chóng (mặc dù hơi hack) sẽ là thay thế dòng

push.apply(this, splice.call(this, 0, count));

với cái này:

(this.push || push).apply(this, (this.splice || splice).call(this, 0, count));

Sử dụng unshift()thay vì push()nhanh hơn gần gấp đôi trong Opera 10, trong khi sự khác biệt trong FF là không đáng kể; mật mã:

Array.prototype.rotate = (function() {
    var unshift = Array.prototype.unshift,
        splice = Array.prototype.splice;

    return function(count) {
        var len = this.length >>> 0,
            count = count >> 0;

        unshift.apply(this, splice.call(this, count % len, len));
        return this;
    };
})();

2
Bộ nhớ đệm tuyệt vời của các Array.prototypephương pháp! +1
James

Triển khai chống đạn rất tốt nhưng nói chung tôi muốn dựa vào các trường hợp ngoại lệ, hoặc chỉ phản hồi đơn giản, cho việc sử dụng không tốt. Điều này để giữ cho mã sạch và nhanh. Người dùng có trách nhiệm chuyển các thông số chính xác hoặc trả hậu quả. Tôi không thích hình phạt dành cho người dùng tốt. Ngoài ra, điều này là hoàn hảo vì nó thực hiện sửa đổi Mảng theo yêu cầu và nó không mắc phải lỗi trong quá trình thực hiện của tôi mà đã chuyển một mảng thay vì các phần tử riêng lẻ như bạn đã lưu ý. Trả lại điều này cũng tốt hơn để cho phép chuỗi. Vì vậy, cảm ơn.
Jean Vincent,

Chi phí của việc đóng ở đây, chỉ để đẩy và ghép bộ đệm là bao nhiêu?
Jean Vincent,

@Jean: tốt, các bao đóng nắm bắt toàn bộ chuỗi phạm vi; miễn là hàm bên ngoài là cấp cao nhất, tác động sẽ không đáng kể và dù sao thì nó cũng là O (1), trong khi tra cứu các phương thức Array là O (n) trong số lần gọi; tối ưu hóa việc triển khai có thể nội tuyến tra cứu nên sẽ không có bất kỳ khoản lãi, nhưng với những nhà chú giải chứ không phải ngu ngốc chúng tôi đã phải đối phó với một thời gian dài, bộ nhớ đệm biến trong một phạm vi thấp hơn có thể có một tác động đáng kể
Christoph

1
Điều này sẽ không xoay các mảng lớn. Tôi đã kiểm tra hôm nay và nó chỉ có thể xử lý một mảng có độ dài 250891 mục. Rõ ràng số lượng đối số bạn có thể truyền theo applyphương thức bị giới hạn với một kích thước ngăn xếp cuộc gọi nhất định. Trong điều kiện ES6, toán tử spread cũng sẽ gặp phải vấn đề kích thước ngăn xếp tương tự. Dưới đây tôi đưa ra một phương pháp bản đồ để làm điều tương tự. Nó chậm hơn nhưng hoạt động với hàng triệu mặt hàng. Bạn cũng có thể triển khai bản đồ bằng một vòng lặp for hoặc while đơn giản và nó sẽ nhanh hơn nhiều.
Redu

144

Bạn có thể sử dụng push(), pop(), shift()unshift()phương pháp:

function arrayRotate(arr, reverse) {
  if (reverse) arr.unshift(arr.pop());
  else arr.push(arr.shift());
  return arr;
}

sử dụng:

arrayRotate(['h','e','l','l','o']);       // ['e','l','l','o','h'];
arrayRotate(['h','e','l','l','o'], true); // ['o','h','e','l','l'];

Nếu bạn cần counttranh luận, hãy xem câu trả lời khác của tôi: https://stackoverflow.com/a/33451102 🖤🧡💚💙💜


Nó không có số đếm mặc dù đó không phải là một vấn đề lớn tùy thuộc vào việc sử dụng.
JustGage

@JustGage, tôi công bố câu trả lời khác với sự hỗ trợ đếm stackoverflow.com/questions/1985260/...
Yukulélé

2
Chỉ cần lưu ý: hãy cẩn thận rằng phương thức này là xâm nhập và nó sẽ thao túng mảng được gửi dưới dạng tham số. Không khó khăn để giải quyết sao chép mảng trước khi gửi nó
fguillen

Đây là một phiên bản trong nguyên cảo với trái biến đúng stackblitz.com/edit/typescript-vtcmhp
Dživo Jelić

38

Tôi có thể sẽ làm một cái gì đó như thế này:

Array.prototype.rotate = function(n) {
    return this.slice(n, this.length).concat(this.slice(0, n));
}

Chỉnh sửa     Đây là một phiên bản đột biến:

Array.prototype.rotate = function(n) {
    while (this.length && n < 0) n += this.length;
    this.push.apply(this, this.splice(0, n));
    return this;
}

hãy nhớ rằng hàm này giữ cho mảng ban đầu không thay đổi
Christoph

Nó cần phải sửa đổi Mảng ban đầu (cái này), giống như push, pop, shift, unshift, concat và splice. Khác với điều này là hợp lệ.
Jean Vincent,

@Gumbo, tại sao lại có vòng lặp while? Chúng ta không cần làm cho n dương, mối nối cũng hoạt động với giá trị âm. Cuối cùng, đây là đúng nhưng khá nhiều phiên bản Christoph đã làm đúng đầu tiên mà không có cảnh báo quá tải.
Jean Vincent,

@Gumbo, đính chính về nhận xét trước đây của tôi, số âm chỉ hoạt động với phiên bản splice (n. This.length). Sử dụng splice (0, n) như trong phiên bản của bạn, yêu cầu số nguyên dương.
Jean Vincent,

1
Tôi đã thêm vào n = n % this.lengthtrước câu lệnh trả về để xử lý các số âm và / hoặc ngoài ranh giới.
iedmrc

25

Hàm này hoạt động theo cả hai cách và hoạt động với bất kỳ số nào (ngay cả với số lớn hơn độ dài mảng):

function arrayRotate(arr, count) {
  count -= arr.length * Math.floor(count / arr.length);
  arr.push.apply(arr, arr.splice(0, count));
  return arr;
}

sử dụng:

for(let i = -6 ; i <= 6 ; i++) {
  console.log(arrayRotate(["🧡","💚","💙","💜","🖤"], i), i);
}

kết quả:

[ "🖤", "🧡", "💚", "💙", "💜" ]    -6
[ "🧡", "💚", "💙", "💜", "🖤" ]    -5
[ "💚", "💙", "💜", "🖤", "🧡" ]    -4
[ "💙", "💜", "🖤", "🧡", "💚" ]    -3
[ "💜", "🖤", "🧡", "💚", "💙" ]    -2
[ "🖤", "🧡", "💚", "💙", "💜" ]    -1
[ "🧡", "💚", "💙", "💜", "🖤" ]    0
[ "💚", "💙", "💜", "🖤", "🧡" ]    1
[ "💙", "💜", "🖤", "🧡", "💚" ]    2
[ "💜", "🖤", "🧡", "💚", "💙" ]    3
[ "🖤", "🧡", "💚", "💙", "💜" ]    4
[ "🧡", "💚", "💙", "💜", "🖤" ]    5
[ "💚", "💙", "💜", "🖤", "🧡" ]    6

Sử dụng hàm này, tôi đã gặp sự cố vì nó làm thay đổi mảng ban đầu. Tôi đã tìm thấy một số cách giải quyết ở đây: stackoverflow.com/questions/14491405
ognockocaten

1
@ognockocaten Đây không phải là vấn đề, đó là cách chức năng này hoạt động. nếu bạn muốn giữ lại mảng ban đầu không thay đổi gì, sao chép nó trước:var arr2 = arrayRotate(arr.slice(0), 5)
Yukulélé

Đây thực sự là một câu trả lời tuyệt vời, và nó đã giúp tôi. Tuy nhiên, cung cấp một giải pháp thay thế không làm thay đổi mảng ban đầu sẽ là tốt nhất. Chắc chắn thật dễ dàng để chỉ tạo một bản sao của bản gốc, đây không phải là phương pháp hay nhất vì nó sử dụng bộ nhớ một cách không cần thiết.
web kết hợp

@Hybridwebdev sử dụngconst immutatableArrayRotate = (arr, count) => ArrayRotate(arr.clone(), count)
Yukulélé

8

Vì vậy, nhiều câu trả lời trong số này có vẻ quá phức tạp và khó đọc. Tôi không nghĩ rằng tôi đã thấy bất cứ ai sử dụng mối nối với concat ...

function rotateCalendar(){
    var cal=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    cal=cal.concat(cal.splice(0,new Date().getMonth()));
    console.log(cal);  // return cal;
}

Kết quả đầu ra console.log (* được tạo vào tháng 5):

["May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr"]

Đối với tính nhỏ gọn, tôi có thể cung cấp một số hàm một lớp chung chung (không tính phần console.log | return). Chỉ cần cung cấp cho nó mảng và giá trị đích trong các đối số.

Tôi kết hợp các hàm này thành một cho một chương trình trò chơi bài bốn người chơi trong đó mảng là ['N', 'E', 'S', 'W']. Tôi để chúng riêng biệt trong trường hợp bất kỳ ai muốn sao chép / dán cho nhu cầu của họ. Với mục đích của mình, tôi sử dụng các chức năng khi tìm kiếm lượt tiếp theo để chơi / hành động trong các giai đoạn khác nhau của trò chơi (Pinochle). Tôi không bận tâm đến việc kiểm tra tốc độ, vì vậy nếu ai đó muốn, vui lòng cho tôi biết kết quả.

* Lưu ý, sự khác biệt duy nhất giữa các chức năng là "+1".

function rotateToFirst(arr,val){  // val is Trump Declarer's seat, first to play
    arr=arr.concat(arr.splice(0,arr.indexOf(val)));
    console.log(arr); // return arr;
}
function rotateToLast(arr,val){  // val is Dealer's seat, last to bid
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+1));
    console.log(arr); // return arr;
}

chức năng kết hợp ...

function rotateArray(arr,val,pos){
    // set pos to 0 if moving val to first position, or 1 for last position
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+pos));
    return arr;
}
var adjustedArray=rotateArray(['N','E','S','W'],'S',1);

AdjustArray =

W,N,E,S

1
Câu trả lời hay đấy. Có phải từ trước khi bạn vượt qua mặt tối (PHP)?
Nick

... Tôi sinh ra trong bóng tối.
mickmackusa

:) btw Tôi đã sử dụng nó ở đây
Nick

6

Sử dụng spread của ES6 cho một ví dụ bất biến ...

[...array.slice(1, array.length), array[0]]

[array[array.items.length -1], ...array.slice(0, array.length -1)]

Nó có lẽ không phải là hiệu quả nhất mặc dù nhưng nó ngắn gọn.


5

Giải pháp dễ dàng với cắt và cấu trúc lại:

const rotate = (arr, count = 1) => {
  return [...arr.slice(count, arr.length), ...arr.slice(0, count)];
};

const arr = [1,2,3,4,5];

console.log(rotate(arr, 1));  // [2, 3, 4, 5, 1]
console.log(rotate(arr, 2));  // [3, 4, 5, 1, 2]
console.log(rotate(arr, -2)); // [4, 5, 1, 2, 3]
console.log(rotate(arr, -1)); // [5, 1, 2, 3, 4]


2
Điều này thực sự tốt đẹp! Bạn có thể tránh phải kiểm tra count === 0bằng cách đặt giá trị mặc định. Điều đó sẽ cho phép bạn biến điều này thành một lớp lót:const rotate = (arr, n = 1) => [...arr.slice(n, arr.length), ...arr.slice(0, n)];
Nick F

@NickF rất hay, cảm ơn bạn! Không nghĩ theo cách này.
kashandr

4

Đây là một cách rất đơn giản để chuyển các mục trong một mảng:

function rotate(array, stepsToShift) {

    for (var i = 0; i < stepsToShift; i++) {
        array.unshift(array.pop());
    }

    return array;
}

3

@Christoph, bạn đã tạo một mã sạch, nhưng chậm nhất 60% so với mã mà tôi tìm thấy. Nhìn vào kết quả trên jsPerf: http://jsperf.com/js-rotate-array/2 [Chỉnh sửa] OK bây giờ có nhiều trình duyệt hơn mà không phải là phương pháp phù thủy rõ ràng là tốt nhất

var rotateArray = function(a, inc) {
    for (var l = a.length, inc = (Math.abs(inc) >= l && (inc %= l), inc < 0 && (inc += l), inc), i, x; inc; inc = (Math.ceil(l / inc) - 1) * inc - l + (l = inc))
    for (i = l; i > inc; x = a[--i], a[i] = a[i - inc], a[i - inc] = x);
    return a;
};

var array = ['a','b','c','d','e','f','g','h','i'];

console.log(array);
console.log(rotateArray(array.slice(), -1)); // Clone array with slice() to keep original

@molokocolo Theo trực giác, giải pháp của bạn sẽ chạy chậm hơn vì mức tăng sẽ cao hơn vì bạn đang sử dụng một vòng lặp. Tôi đã cập nhật trường hợp thử nghiệm của bạn với một tăng 5 và nó dường như sau đó chạy chậm hơn: jsperf.com/js-rotate-array/3
Jean Vincent

Tôi không hình những gì đang làm rotateArray () chức năng ^^ duy nhất mà nó làm việc :) (đó là một mã lạ!) Chrome cư xử gần như trái ngược với Firefox ...
molokoloco

Phương thức này rất thú vị, nó hoạt động bằng cách hoán đổi các tham chiếu phần tử trong mảng. Kích thước mảng không bao giờ thay đổi, đó là lý do tại sao nó rất nhanh với nhược điểm của việc sử dụng vòng lặp. Điều thú vị mà nó cho thấy là Firefox là nhanh nhất tại các vòng lặp trong khi chrome là nhanh nhất trong các thao tác mảng.
Jean Vincent,

1
Sau khi phân tích mã, tôi đã tìm ra một điểm yếu cho thấy giải pháp này chỉ nhanh hơn đối với các mảng nhỏ hơn và số vòng quay nhất định: jsperf.com/js-rotate-array/5 Trong số tất cả các trình duyệt nhanh nhất vẫn là Google Chrome với splice / unshift giải pháp. Điều này có nghĩa rằng đây thực sự là vấn đề tối ưu hóa phương thức Array mà Chrome làm tốt hơn các trình duyệt khác.
Jean Vincent

3

xem http://jsperf.com/js-rotate-array/8

function reverse(a, from, to) {
  --from;
  while (++from < --to) {
    var tmp = a[from];
    a[from] = a[to];
    a[to] = tmp;
  }
}

function rotate(a, from, to, k) {
  var n = to - from;
  k = (k % n + n) % n;
  if (k > 0) {
    reverse(a, from, from + k);
    reverse(a, from + k, to);
    reverse(a, from, to);
  }
}

3

Khi tôi không thể tìm thấy một đoạn mã được tạo sẵn để bắt đầu danh sách các ngày với 'hôm nay', tôi đã làm như thế này (không hoàn toàn chung chung, có lẽ kém tinh tế hơn nhiều so với các ví dụ trên, nhưng đã thực hiện được công việc):

//returns 7 day names with today first
function startday() {
    const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    let today = new Date();
    let start = today.getDay(); //gets day number
    if (start == 0) { //if Sunday, days are in order
        return days
    }
    else { //if not Sunday, start days with today
        return days.slice(start).concat(days.slice(0,start))
    }
}

Nhờ một lập trình viên giỏi hơn tôi, nó ngắn hơn một hoặc hai dòng so với nỗ lực ban đầu của tôi, nhưng mọi nhận xét thêm về hiệu quả đều được hoan nghênh.


3
function rotate(arr, k) {
for (var i = 0; i < k+1; i++) {
    arr.push(arr.shift());
}
return arr;
}
//k work as an index array
console.log(rotate([1, 2, 7, 4, 5, 6, 7], 3)); //[5,6,7,1,2,7,4]
console.log(rotate([-1, -100, 3, 99], 2));     //[99,-1,-100,3]

3
// Example of array to rotate
let arr = ['E', 'l', 'e', 'p', 'h', 'a', 'n', 't'];

// Getting array length
let length = arr.length;

// rotation < 0 (move left), rotation > 0 (move right)
let rotation = 5;

// Slicing array in two parts
let first  = arr.slice(   (length - rotation) % length, length); //['p', 'h', 'a' ,'n', 't']
let second = arr.slice(0, (length - rotation) % length); //['E', 'l', 'e']

// Rotated element
let rotated = [...first, ...second]; // ['p', 'h', 'a' ,'n', 't', 'E', 'l', 'e']

Trong một dòng mã:

let rotated = [...arr.slice((length - rotation) % length, length), ...arr.slice(0, (length - rotation) % length)];

2

Câu trả lời được chấp nhận có một lỗ hổng là không thể xử lý các mảng lớn hơn kích thước ngăn xếp cuộc gọi phụ thuộc vào phiên nhưng phải khoảng 100 ~ 300K mục. Ví dụ: trong phiên Chrome hiện tại mà tôi đã thử là 250891. Trong nhiều trường hợp, bạn thậm chí có thể không biết kích thước mảng có thể phát triển động thành. Vì vậy, đó là một vấn đề nghiêm trọng.

Để khắc phục hạn chế này, tôi đoán một phương pháp thú vị là sử dụng Array.prototype.map()và ánh xạ các phần tử bằng cách sắp xếp lại các chỉ số theo kiểu hình tròn. Phương thức này nhận một đối số nguyên. Nếu đối số này là tích cực, nó sẽ xoay theo hướng tăng của chỉ số và nếu là âm theo hướng giảm của chỉ số. Điều này chỉ có độ phức tạp về thời gian O (n) và sẽ trả về một mảng mới mà không làm thay đổi mảng mà nó được gọi trong khi xử lý hàng triệu mục mà không gặp bất kỳ vấn đề gì. Hãy xem nó hoạt động như thế nào;

Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this
                  : n > 0 ? this.map((e,i,a) => a[(i + n) % len])
                          : this.map((e,i,a) => a[(len - (len - i - n) % len) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(2);
console.log(JSON.stringify(b));
    b = a.rotate(-1);
console.log(JSON.stringify(b));

Thực ra sau khi tôi bị chỉ trích về hai vấn đề như sau;

  1. Không cần điều kiện cho đầu vào tích cực hoặc tiêu cực vì nó cho thấy sự vi phạm DRY. Bạn có thể làm điều này với một bản đồ vì mọi n âm đều có giá trị tương đương dương (Hoàn toàn đúng ..)
  2. Một hàm Array sẽ thay đổi mảng hiện tại hoặc tạo một mảng mới, hàm của bạn có thể thực hiện tùy thuộc vào việc chuyển đổi có cần thiết hay không (Hoàn toàn đúng ..)

Tôi đã quyết định sửa đổi mã như sau;

Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this.slice()
                  : this.map((e,i,a) => a[(i + (len + n % len)) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(10);
console.log(JSON.stringify(b));
    b = a.rotate(-10);
console.log(JSON.stringify(b));

Sau đó một lần nữa; tất nhiên các bộ chức năng JS như Array.prototype.map()chậm hơn so với các trình tương đương của chúng được mã hóa bằng JS đơn giản. Để đạt được hiệu suất tăng hơn 100%, điều sau có thể là lựa chọn của Array.prototype.rotate()tôi nếu tôi cần xoay một mảng trong mã sản xuất như mảng mà tôi đã sử dụng trong nỗ lực của mìnhString.prototype.diff()

Array.prototype.rotate = function(n){
  var len = this.length,
      res = new Array(this.length);
  if (n % len === 0) return this.slice();
  else for (var i = 0; i < len; i++) res[i] = this[(i + (len + n % len)) % len];
  return res;
};

shift () và splice (), chậm như chúng có thể thực sự nhanh hơn so với sử dụng map () hoặc bất kỳ phương thức nào sử dụng tham số hàm. shift () và splice () được khai báo nhiều hơn cũng dễ dàng tối ưu hóa hơn trong dài hạn.
Jean Vincent

@Jean Vincent Vâng, bạn nói đúng .. bản đồ trên thực tế có thể chậm hơn nhưng tôi tin rằng logic xoay vòng lý tưởng là thế này. Tôi chỉ cố gắng đưa ra cách tiếp cận hợp lý. Bản đồ có thể dễ dàng được đơn giản hóa bằng vòng lặp for và sẽ dẫn đến cải thiện đáng kể về tốc độ ... Tuy nhiên, thực tế là, câu trả lời được chấp nhận có một cách tiếp cận được phát triển với một số ngôn ngữ khác. Nó không lý tưởng cho JS .... thậm chí sẽ không chạy khi kích thước mảng lớn hơn kích thước ngăn xếp cuộc gọi. (trong trường hợp của tôi và trong phiên này hóa ra chỉ có 250891 mặt hàng giới hạn) Vì vậy, có điều đó ..!
Redu

Bạn nói đúng về kích thước ngăn xếp cuộc gọi áp dụng có thể vượt quá, đây là đối số hợp lệ hơn tốc độ, hãy cân nhắc chỉnh sửa câu trả lời của bạn để nhấn mạnh điểm này với việc cải thiện thụt lề.
Jean Vincent

2

Hàm này nhanh hơn một chút so với câu trả lời được chấp nhận cho các mảng nhỏ nhưng nhanh hơn NHIỀU cho các mảng lớn. Hàm này cũng cho phép một số phép quay tùy ý lớn hơn độ dài của mảng, đây là một hạn chế của hàm gốc.

Cuối cùng, câu trả lời được chấp nhận xoay theo hướng ngược lại như được mô tả.

const rotateForEach = (a, n) => {
    const l = a.length;
    a.slice(0, -n % l).forEach(item => a.push( item ));
    return a.splice(n % l > 0 ? (-n % l) : l + (-n % l));
}

Và chức năng tương đương (dường như cũng có một số lợi ích về hiệu suất):

const rotateReduce = (arr, n) => {
    const l = arr.length;
    return arr.slice(0, -n % l).reduce((a,b) => {
        a.push( b );
        return a;
    }, arr).splice(n % l> 0 ? l + (-n % l) : -n % l);
};

Bạn có thể xem bảng phân tích hiệu suất tại đây.


Thật không may, nó không hoạt động, nó thu nhỏ mảng xuống kích thước bằng không, điều này giải thích tại sao nó nhanh hơn nhiều, sau lần chạy đầu tiên so với bất kỳ triển khai chính xác nào khác, đặc biệt là trên các mảng lớn hơn. Trong bài kiểm tra điểm chuẩn của bạn, nếu bạn hoán đổi thứ tự các bài kiểm tra và hiển thị a.length ở cuối, bạn sẽ thấy vấn đề.
Jean Vincent

2

BIÊN TẬP:: Này, hóa ra có quá nhiều lần lặp lại xảy ra. Không có vòng lặp, không phân nhánh.

Vẫn hoạt động với n âm cho phép quay phải và n dương cho phép quay trái cho bất kỳ kích thước n nào, Không có đột biến

function rotate(A,n,l=A.length) {
  const offset = (((n % l) + l) %l)
  return A.slice(offset).concat(A.slice(0,offset))
}

Đây là phiên bản chơi gôn mã cho tiếng cười khúc khích

const r = (A,n,l=A.length,i=((n%l)+l)%l)=>A.slice(i).concat(A.slice(0,i))

EDIT1 :: * Thực hiện không nhánh, không đột biến.

Vậy này, hóa ra tôi đã có một chi nhánh mà tôi không cần nó. Đây là một giải pháp làm việc. âm num = xoay phải bởi | num | num dương = xoay trái theo num

function r(A,n,l=A.length) {
  return A.map((x,i,a) => A[(((n+i)%l) + l) % l])
}

Phương trình ((n%l) + l) % lánh xạ chính xác các số dương và số âm của bất kỳ giá trị lớn nào tùy ý của n

NGUYÊN

Xoay trái và phải. Xoay trái với tích cực n, xoay phải với tiêu cực n.

Hoạt động cho các đầu vào lớn một cách khiêu dâm của n.

Không có chế độ đột biến. Quá nhiều đột biến trong những câu trả lời này.

Ngoài ra, ít thao tác hơn hầu hết các câu trả lời. Không bật, không đẩy, không mối nối, không dịch chuyển.

const rotate = (A, num ) => {
   return A.map((x,i,a) => {
      const n = num + i
      return n < 0 
        ? A[(((n % A.length) + A.length) % A.length)]
        : n < A.length 
        ? A[n] 
        : A[n % A.length]
   })
}

hoặc là

 const rotate = (A, num) => A.map((x,i,a, n = num + i) => 
  n < 0
    ? A[(((n % A.length) + A.length) % A.length)]
    : n < A.length 
    ? A[n] 
    : A[n % A.length])

//test
rotate([...Array(5000).keys()],4101)   //left rotation
rotate([...Array(5000).keys()],-4101000)  //right rotation, num is negative

// will print the first index of the array having been rotated by -i
// demonstrating that the rotation works as intended
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,-i)[0])
}) 
// prints even numbers twice by rotating the array by i * 2 and getting the first value
//demonstrates the propper mapping of positive number rotation when out of range
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,i*2)[0])
})

Giải trình:

ánh xạ từng chỉ mục của A với giá trị tại độ lệch chỉ mục. Trong trường hợp này

offset = num

nếu offset < 0sau đó offset + index + positive length of Asẽ trỏ đến phần bù nghịch đảo.

nếu offset > 0 and offset < length of Asau đó chỉ cần ánh xạ chỉ mục hiện tại với chỉ số bù của A.

Nếu không, hãy điều chỉnh độ lệch và độ dài để ánh xạ độ lệch trong các giới hạn của mảng.

Lấy ví dụ offset = 4offset = -4.

Khi nào offset = -4, và A = [1,2,3,4,5], đối với mỗi chỉ mục, offset + indexsẽ làm cho độ lớn (hoặc Math.abs(offset)) nhỏ hơn.

Hãy giải thích phép tính cho chỉ số của n âm trước. A[(((n % A.length) + A.length) % A.length)+0]và bị đe dọa. Đừng như vậy. Tôi mất 3 phút trong Repl để giải quyết nó.

  1. Chúng tôi biết nlà tiêu cực bởi vì trường hợp là n < 0. Nếu số lớn hơn phạm vi của Mảng, n % A.lengthsẽ ánh xạ nó vào phạm vi.
  2. n + A.lengthcộng số đó A.lengthđể bù lại số tiền đúng.
  3. Chúng tôi biết nlà tiêu cực bởi vì trường hợp là n < 0. n + A.lengthcộng số đó A.lengthđể bù lại số tiền đúng.
  4. Tiếp theo Ánh xạ nó đến phạm vi độ dài của A bằng cách sử dụng modulo. Điều chỉnh thứ hai là cần thiết để ánh xạ kết quả của phép tính thành một phạm vi có thể lập chỉ mục

    nhập mô tả hình ảnh ở đây

  5. Chỉ số đầu tiên: -4 + 0 = -4. A.length = 5. A.length - 4 = 1. A 2 là 2. Chỉ số ánh xạ từ 0 đến 2.[2,... ]

  6. Chỉ số tiếp theo, -4 + 1 = -3. 5 + -3 = 2. A 2 là 3. Chỉ số bản đồ từ 1 đến 3.[2,3... ]
  7. Vân vân.

Quy trình tương tự áp dụng cho offset = 4. Khi nào offset = -4, và A = [1,2,3,4,5], đối với mỗi chỉ số, offset + indexsẽ làm cho độ lớn lớn hơn.

  1. 4 + 0 = 0. Ánh xạ A [0] với giá trị tại A [4].[5...]
  2. 4 + 1 = 5, 5 nằm ngoài giới hạn khi lập chỉ mục, vì vậy ánh xạ A 2 với giá trị ở phần còn lại của 5 / 5, là 0. A 2 = giá trị tại A [0].[5,1...]
  3. nói lại.

1
Follow a simpler approach of running a loop to n numbers and shifting places upto that element.

function arrayRotateOne(arr, n) {
  for (let i = 0; i < n; i++) {
    arr.unshift(arr.pop());
  }
  return arr;
}
console.log( arrayRotateOne([1,2,3,4,5,6],2));



function arrayRotateOne(arr,n) {
  for(let i=0; i<n;i++){
      arr.push(arr.shift());
      console.log('execute',arr)
    }
     return arr;
 }

console.log (arrayRotateOne ([1,2,3,4,5,6], 2));


1

Giải pháp không đột biến

var arr = ['a','b','c','d']
arr.slice(1,arr.length).concat(arr.slice(0,1)

với đột biến

var arr = ['a','b','c','d']
arr = arr.concat(arr.splice(0,1))

1

Tôi đang chia sẻ giải pháp mà tôi đang sử dụng để quay trên băng chuyền. Nó có thể bị vỡ khi kích thước mảng nhỏ hơn displayCount, nhưng bạn có thể thêm điều kiện bổ sung để ngừng xoay khi nó nhỏ hơn hoặc nối mảng chính * displayCount lần nữa.

function rotate(arr, moveCount, displayCount) {
  const size = arr.length;

  // making sure startIndex is between `-size` and `size`
  let startIndex = moveCount % size;
  if (startIndex < 0) startIndex += size; 

  return [...arr, ...arr].slice(startIndex, startIndex + displayCount);
}

// move 3 to the right and display 4 items
// rotate([1,2,3,4,5], 3, 4) -> [4,5,1,2]

// move 3 to the left and display 4 items
// rotate([1,2,3,4,5], -3, 4) -> [3,4,5,1]

// move 11 to the right and display 4
// rotate([1,2,3,4,5], 3, 4) -> [2,3,4,5]

0

Làm thế nào về việc tăng một bộ đếm và sau đó lấy phần còn lại của một phép chia cho độ dài mảng để đạt được vị trí của bạn.

var i = 0;
while (true);
{
    var position = i % months.length;
    alert(months[position]);
    ++i;
}

Cú pháp ngôn ngữ sang một bên, điều này sẽ hoạt động tốt.


2
Điều này không xoay Array theo bất kỳ cách nào.
Jean Vincent,

1
Đúng (như đã nêu trong câu trả lời), nhưng nó tiết kiệm được việc phải xoay mảng.
tgandrews

1
Nhưng toàn bộ điểm của IS này là xoay một Mảng, theo tiêu đề, không hiển thị một mảng từ một độ lệch nhất định. Nếu bạn sử dụng cùng một vòng lặp để xây dựng lại mảng mới, nó sẽ chậm hơn rất nhiều so với các phiên bản khác sử dụng các phương thức Mảng nguyên bản. Thêm vào đó, bạn đã quên thoát ra khỏi vòng lặp vô hạn.
Jean Vincent

0

Nếu mảng của bạn sẽ lớn và / hoặc bạn sẽ xoay vòng nhiều, bạn có thể muốn xem xét sử dụng danh sách được liên kết thay vì một mảng.


Đồng ý, nhưng câu hỏi liên quan đến Mảng và cụ thể hơn là mở rộng động từ Mảng.
Jean Vincent

0

@molokoloco Tôi cần một chức năng mà tôi có thể định cấu hình để xoay theo một hướng - true cho tiến và false cho lùi. Tôi đã tạo một đoạn mã lấy một hướng, một bộ đếm và một mảng và xuất một đối tượng với bộ đếm tăng dần theo hướng thích hợp cũng như các giá trị trước đó, hiện tại và tiếp theo. Nó KHÔNG sửa đổi mảng ban đầu.

Tôi cũng đã kiểm soát nó so với đoạn mã của bạn và mặc dù nó không nhanh hơn nhưng nó nhanh hơn những đoạn bạn so sánh với đoạn mã của bạn - chậm hơn 21% http://jsperf.com/js-rotate-array/7 .

function directionalRotate(direction, counter, arr) {
  counter = direction ? (counter < arr.length - 1 ? counter + 1 : 0) : (counter > 0 ? counter - 1 : arr.length - 1)
  var currentItem = arr[counter]
  var priorItem = arr[counter - 1] ? arr[counter - 1] : arr[arr.length - 1]
  var nextItem = arr[counter + 1] ? arr[counter + 1] : arr[0]
  return {
    "counter": counter,
    "current": currentItem,
    "prior": priorItem,
    "next": nextItem
  }
}
var direction = true // forward
var counter = 0
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];

directionalRotate(direction, counter, arr)

Thao tác này không xoay Mảng. Để trả lời câu hỏi này, bạn cần trả về một Mảng nơi có mục đầu tiên mà nó tại quầy.
Jean Vincent

0

Tôi đến muộn nhưng tôi có một viên gạch để thêm vào những câu trả lời hay này. Tôi được yêu cầu viết mã một hàm như vậy và lần đầu tiên tôi làm:

Array.prototype.rotate = function(n)
{
    for (var i = 0; i < n; i++)
    {
        this.push(this.shift());
    }
    return this;
}

Nhưng nó dường như kém hiệu quả hơn so với sau khi nlớn:

Array.prototype.rotate = function(n)
{
    var l = this.length;// Caching array length before map loop.

    return this.map(function(num, index) {
        return this[(index + n) % l]
    });
}

0

Tôi không chắc đây có phải là cách hiệu quả nhất hay không nhưng tôi thích cách nó đọc, nó đủ nhanh cho hầu hết các tác vụ lớn vì tôi đã thử nghiệm nó trên sản xuất ...

function shiftRight(array) {
  return array.map((_element, index) => {
    if (index === 0) {
      return array[array.length - 1]
    } else return array[index - 1]
  })
}

function test() {
  var input = [{
    name: ''
  }, 10, 'left-side'];
  var expected = ['left-side', {
    name: ''
  }, 10]
  var actual = shiftRight(input)

  console.log(expected)
  console.log(actual)

}

test()


0

Gốc, nhanh, nhỏ, có ngữ nghĩa, hoạt động trên các công cụ cũ và "có thể chạy được".

function rotateArray(offset, array) {
    offset = -(offset % array.length) | 0 // ensure int
    return array.slice(offset).concat(
        array.slice(0, offset)
    )
}

0

** Sử dụng phiên bản JS mới nhất, chúng tôi có thể xây dựng nó một cách dễ dàng **

 Array.prototype.rotateLeft = function (n) {
   this.unshift(...this.splice(-(n), n));
    return this
  }

ở đây di chuyển: số lần quay , một Mảng mà bạn có thể chuyển số ngẫu nhiên

let a = [1, 2, 3, 4, 5, 6, 7];
let moves = 4;
let output = a.rotateLeft(moves);
console.log("Result:", output)

0

Arraytrong JS có phương thức tích hợp bên dưới có thể được sử dụng để xoay một mảng khá dễ dàng và rõ ràng là những phương thức này về bản chất là bất biến .

  • push: Chèn mục vào cuối mảng.
  • pop: Loại bỏ mục khỏi phần cuối của mảng.
  • unshift: Chèn mục vào đầu mảng.
  • shift: Loại bỏ mục khỏi đầu mảng.

Giải pháp dưới đây ( ES6) nhận hai đối số, mảng cần được xoay và n, số lần mảng cần được xoay.

const rotateArray = (arr, n) => {
  while(arr.length && n--) {
    arr.unshift(arr.pop());
  }
  return arr;
}

rotateArray(['stack', 'overflow', 'is', 'Awesome'], 2) 
// ["is", "Awesome", "stack", "overflow"]

Nó có thể được thêm vào Array.prototype và có thể được sử dụng trên tất cả ứng dụng của bạn

Array.prototype.rotate = function(n) {
 while(this.length && n--) {
   this.unshift(this.pop());
 }
 return this;
}
[1,2,3,4].rotate(3); //[2, 3, 4, 1]

0

Sử dụng vòng lặp for. Đây là các bước

  1. Lưu trữ phần tử đầu tiên của mảng dưới dạng biến tạm thời.
  2. Sau đó hoán đổi từ trái sang phải.
  3. Sau đó gán biến tạm thời cho phần tử cuối cùng của mảng.
  4. Lặp lại các bước này cho số lần quay.

function rotateLeft(arr, rotations) {
    let len = arr.length;
    for(let i=0; i<rotations; i++){ 
        let temp = arr[0];
        for(let i=0; i< len; i++){
            arr[i]=arr[i+1];
        }
        arr[len-1]=temp;
    }
    return arr;
}

let arr = [1,2,3,4,5];

let rotations = 3;
let output = rotateLeft(arr, rotations);
console.log("Result Array => ", output);



-2

Không chắc về hiệu quả nhưng tôi sẽ làm theo cách không gây đột biến này:

	Array.prototype.rotate = function( n ) {
  
		 return this.map( (item, index)=> this[ (this.length + index + n)%this.length ] )
	}


Vì vậy, phần đầu câu trả lời của bạn “Không chắc chắn về hiệu quả” giống như bạn nói “Tôi không biết điều này có trả lời được câu hỏi của bạn hay không”. Câu hỏi ban đầu không hỏi "cách tốt để xoay một mảng là gì", họ đặc biệt hỏi về độ nhỏ gọn và tốc độ (hiệu quả về bộ nhớ và thời gian tương ứng). Câu hỏi là tất cả về hiệu quả và bạn chưa trả lời được thành phần của nó.
richardpringle
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.