JavaScript에서 배열의 요소 회전


86

JavaScript 배열을 회전하는 가장 효율적인 방법이 무엇인지 궁금합니다.

양수 n는 배열을 오른쪽으로, 음수 n는 왼쪽으로 회전하는 () 이 솔루션을 생각해 냈습니다 -length < n < length.

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

그러면 다음과 같이 사용할 수 있습니다.

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

위의 원래 버전에는 아래 주석에서 Christoph 가 지적한대로 결함 이 있습니다. 올바른 버전은 다음과 같습니다 (추가 반환은 연결을 허용 함).

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

JavaScript 프레임 워크의 맥락에서 더 간결하고 빠른 솔루션이 있습니까? (제안 된 버전 중 어느 것도 더 작거나 빠르지 않습니다)

배열 회전이 내장 된 JavaScript 프레임 워크가 있습니까? (아직도 아무도 대답하지 않음)


1
나는 당신의 모범이 무엇을해야하는지 이해하지 못합니다. months[new Date().getMonth()]이달의 이름을 얻기 위해 사용 하는 것이 어떻습니까?
Gumbo

1
@Jean : 코드가 깨졌습니다. 당신이하는 방식은 스 플라이 싱 된 요소를 개별적으로가 아닌 배열로 이동하지 않습니다. apply()구현 작업을 수행하는 데 사용해야 합니다
Christoph

오늘 로테이션은이 목록을 표시하도록 월을 수정합니다 ( Dec첫 번째 위치에 있음) :["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"]
Jean Vincent

@Christoph, 당신 말이 맞습니다. 이것은 일반적인 회전 기능으로 작동하지 않을 것입니다. 바로 뒤에 문자열로 변환하는 데 사용되는 경우에만 작동합니다.
Jean Vincent

를 통해 당신이 당신의 버전을 수정할 수 있습니다 @Jean Array.prototype.unshift.apply(this, this.splice(...))- 내 버전은 같은 일을하지만, 사용 push()대신에unshift()
크리스토프

답변:


62

배열을 변경하는 형식이 안전한 일반 버전 :

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

의견에서 Jean은 코드가 push()및의 오버로딩을 지원하지 않는다는 문제를 제기했습니다 splice(). 나는 이것이 정말로 유용하다고 생각하지 않지만 (주석 참조) 빠른 해결책 (하지만 약간의 해킹)은 줄을 바꾸는 것입니다.

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

이것으로 :

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

unshift()대신을 사용 하는 push()것은 Opera 10에서 거의 두 배나 빠르지 만 FF의 차이는 무시할 만했습니다. 코드:

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
Array.prototype메소드의 멋진 캐싱 ! +1
James

매우 좋은 방탄 구현이지만 일반적으로 잘못된 사용을 위해 예외 또는 단순한 잘못된 응답에 의존하는 것을 선호합니다. 이것은 코드를 깨끗하고 빠르게 유지하기위한 것입니다. 올바른 매개 변수를 전달하거나 결과를 지불하는 것은 사용자의 책임입니다. 좋은 사용자에 대한 벌칙이 마음에 들지 않습니다. 그 외에는 요청에 따라 Array를 수정하고 지적한 것처럼 개별 요소 대신 배열을 이동하지 않은 내 구현의 결함으로 고통받지 않기 때문에 완벽합니다. 이것을 반환하면 체인을 허용하는 것이 더 좋습니다. 감사합니다.
Jean Vincent

푸시 및 스플 라이스를 캐시하기 위해 여기서 폐쇄 비용은 얼마입니까?
Jean Vincent

@Jean : 클로저는 전체 범위 체인을 캡처합니다. 외부 함수가 최상위 수준 인 한 영향은 무시할 수 있어야하며 어쨌든 O (1) 인 반면, Array 메서드를 찾는 것은 호출 횟수에서 O (n)입니다. 구현을 최적화하면 조회가 인라인 될 수 있으므로 이득이 없을 것입니다. 그러나 우리가 오랫동안 다루어야했던 다소 어리석은 인터프리터를 사용하면 더 낮은 범위의 변수를 캐싱하면 상당한 영향을 미칠 수 있습니다
Christoph

1
이것은 큰 배열을 회전시키지 않습니다. 오늘 확인했는데 길이가 250891 개 항목의 배열 만 처리 할 수있었습니다. 분명히 apply메서드로 전달할 수있는 인수의 수는 특정 호출 스택 크기로 제한됩니다. ES6 측면에서 스프레드 연산자도 동일한 스택 크기 문제를 겪습니다. 아래에서는 동일한 작업을 수행하는지도 방법을 제공합니다. 느리지 만 수백만 개의 항목에서 작동합니다. 간단한 for 또는 while 루프로 맵을 구현할 수도 있으며 훨씬 빨라질 것입니다.
Redu

148

당신이 사용할 수있는 push(), pop(), shift()unshift()방법 :

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

용법:

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

count논쟁 이 필요하면 내 다른 답변을 참조하십시오 : https://stackoverflow.com/a/33451102 🖤🧡💚💙💜


용도에 따라 큰 문제는 아니지만 카운트는 없습니다.
JustGage

@JustGage, 나는 카운트 지원과 다른 답변을 게시 stackoverflow.com/questions/1985260/...
Yukulélé

2
참고 :이 메소드는 방해가되며 매개 변수로 전송 된 배열을 조작합니다. 어려운 일이 아니다 해결하기 위해 보내기 전에 배열을 복제 그것
fguillen

왼쪽 오른쪽 변수가있는 typescript 버전입니다. stackblitz.com/edit/typescript-vtcmhp
Dživo Jelić

38

아마도 다음과 같이 할 것입니다.

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

편집     다음은 mutator 버전입니다.

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

이 함수는 원래 배열을 변경하지 않은 상태로 유지합니다
Christoph

push, pop, shift, unshift, concat 및 splice처럼 원래 배열 (this)을 수정해야합니다. 그 외에는 유효합니다.
Jean Vincent

@ 검보, 왜 while 루프입니까? n을 양수로 만들 필요는 없으며 splice는 음수 값으로도 작동합니다. 결국, 이것은 옳지 만 오버로딩 경고없이 먼저 올바르게 된 Christoph 버전입니다.
Jean Vincent

@Gumbo, 내 이전 의견 수정, 음수는 splice (n. this.length) 버전에서만 작동합니다. 버전에서와 같이 splice (0, n)를 사용하려면 양의 정수가 필요합니다.
Jean Vincent

1
n = n % this.length음수 및 / 또는 경계를 벗어난 숫자를 처리하기 위해 return 문 앞에 추가했습니다 .
iedmrc

25

이 함수는 두 가지 방식으로 작동하며 임의의 숫자 (배열 길이보다 큰 숫자 포함)로 작동합니다.

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

용법:

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

결과:

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

이 함수를 사용하면 원래 배열이 변경되기 때문에 문제가 발생했습니다. 나는 여기에 몇 가지 해결 방법을 발견 stackoverflow.com/questions/14491405
ognockocaten

1
@ognockocaten 문제가 아니라이 기능이 작동하는 방식입니다. 당신이 변경되지 않은 원래의 배열을 유지하려는 경우, 전에 복제 :var arr2 = arrayRotate(arr.slice(0), 5)
Yukulélé

이것은 참으로 훌륭한 대답이며 저를 도왔습니다. 그러나 원래 배열을 변경하지 않는 대안을 제공하는 것이 가장 좋습니다. 물론 단순히 원본의 복사본을 만드는 것은 쉽지만 불필요하게 메모리를 사용하기 때문에 모범 사례는 아닙니다.
하이브리드 웹 개발자

@Hybridwebdev 사용const immutatableArrayRotate = (arr, count) => ArrayRotate(arr.clone(), count)
Yukulélé

9

이러한 답변 중 많은 부분이 지나치게 복잡하고 읽기 어려운 것 같습니다. 나는 concat과 함께 splice를 사용하는 사람을 본 적이 없다고 생각합니다 ...

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

console.log 출력 (* 5 월에 생성됨) :

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

간결함과 관련하여 몇 가지 일반적인 한 줄 함수 (console.log | 반환 부분 제외)를 제공 할 수 있습니다. 인수에 배열과 대상 값을 제공하기 만하면됩니다.

나는 배열이 [ 'N', 'E', 'S', 'W'] 인 4 인용 카드 게임 프로그램을 위해 이러한 기능을 하나로 결합합니다. 누군가가 필요에 따라 복사 / 붙여 넣기를 원하는 경우를 대비하여 별도로 두었습니다. 내 목적을 위해 게임의 다른 단계 (Pinochle)에서 플레이 / 액션 옆에있는 차례를 찾을 때 기능을 사용합니다. 나는 속도 테스트에 신경 쓰지 않았으므로 다른 사람이 원하면 결과를 알려주십시오.

*주의, 기능 간의 유일한 차이점은 "+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;
}

조합 기능 ...

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
좋은 대답입니다. 어두운면 (PHP)으로 넘어 가기 전 이었습니까?
Nick

... 나는 어두운면에서 태어났다.
mickmackusa

:) btw 여기서
Nick

6

불변의 예를 위해 ES6의 스프레드 사용 ...

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

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

아마도 가장 효율적이지는 않지만 간결합니다.


5

슬라이스 및 디스트 럭처링을 통한 간편한 솔루션 :

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
이것은 정말 멋지다! count === 0기본값을 설정하여 확인하지 않아도됩니다 . 이를 통해 한 const rotate = (arr, n = 1) => [...arr.slice(n, arr.length), ...arr.slice(0, n)];
Nick F

4

다음은 배열에서 항목을 이동하는 매우 간단한 방법입니다.

function rotate(array, stepsToShift) {

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

    return array;
}

3

@Christoph, 당신은 깨끗한 코드를 작성했지만 내가 찾은 이것보다 60 % 느 립니다. jsPerf에 대한 결과를보십시오 : http://jsperf.com/js-rotate-array/2 [편집] 이제 더 많은 브라우저가 있고 분명하지 않은 마녀 방법이 가장 좋습니다.

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 직관적으로 루프를 사용하고 있기 때문에 증분이 높을수록 솔루션이 느리게 실행됩니다. 나는 5의 증가로 테스트 케이스를 업데이트하고 그 다음에 느리게 실행되는 것처럼 않습니다 jsperf.com/js-rotate-array/3
진 빈센트에게

나는 rotateArray () 함수를하고있는 것이 무엇인지 알지 못한다 ^^ 그것이 작동한다는 것만 :) (이상한 코드입니다!) 크롬은 Firefox와 거의 반대로 작동합니다 ...
molokoloco

이 방법은 매우 흥미 롭습니다. 배열에서 요소 참조를 교체하여 작동합니다. 배열 크기는 절대 변경되지 않으므로 루프 사용의 단점으로 인해 매우 빠릅니다. 흥미로운 점은 Firefox가 루프에서 가장 빠르고 크롬이 배열 조작에서 가장 빠르다는 것입니다.
진 빈센트

1
코드를 분석 한 후이 솔루션이 더 작은 배열과 특정 회전 수에 대해서만 더 빠르다는 것을 보여주는 약점을 발견했습니다. jsperf.com/js-rotate-array/5 모든 브라우저 중에서 가장 빠른 것은 스플 라이스 / 언 시프트 기능이있는 Google 크롬입니다. 해결책. 이것은 Chrome이 다른 브라우저보다 더 잘하는 배열 방법 최적화의 문제라는 것을 의미합니다.
Jean Vincent

3

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

'today'로 요일 목록을 시작하는 기성품 스 니펫을 찾을 수 없을 때 다음과 같이했습니다 (일반적이지 않고 위의 예보다 훨씬 덜 세련되었지만 작업을 수행했습니다).

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

나보다 더 나은 프로그래머의 약간의 리팩토링 덕분에 초기 시도보다 한두 줄 더 짧지 만 효율성에 대한 추가 의견은 환영합니다.


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

한 줄의 코드에서 :

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

2

허용되는 답변은 세션에 따라 다르지만 약 100 ~ 30 만 개의 항목이 있어야하는 호출 스택 크기보다 큰 배열을 처리 할 수 ​​없다는 결함이 있습니다. 예를 들어, 내가 시도한 현재 Chrome 세션에서는 250891이었습니다. 많은 경우 배열이 동적으로 커질 수있는 크기를 알지 못할 수도 있습니다. 그래서 그것은 심각한 문제입니다.

이러한 한계를 극복하기 위해 흥미로운 방법 중 하나 Array.prototype.map()는 인덱스를 순환 방식으로 재 배열하여 요소를 활용 하고 매핑하는 것입니다. 이 메서드는 하나의 정수 인수를 사용합니다. 이 인수가 양수이면 증가하는 인덱스에서 회전하고 음수이면 감소하는 인덱스 방향에서 회전합니다. 이것은 O (n) 시간 복잡도를 가지며 문제없이 수백만 개의 항목을 처리하는 동안 호출 된 배열을 변경하지 않고 새 배열을 반환합니다. 어떻게 작동하는지 보자.

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

실제로 다음과 같은 두 가지 문제에 대해 비판을받은 후

  1. DRY 위반을 나타 내기 때문에 양수 또는 음수 입력에 대한 조건부가 필요하지 않습니다. 모든 음수 n이 양수 등가물을 갖기 때문에 하나의 맵으로이 작업을 수행 할 수 있습니다 (완전히 맞습니다 ..).
  2. Array 함수는 현재 배열을 변경하거나 새 배열을 만들어야합니다. 함수는 시프트가 필요한지 여부에 따라 수행 할 수 있습니다 (완전히 맞습니다 ..)

다음과 같이 코드를 수정하기로 결정했습니다.

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

그리고 다시; 물론 같은 JS 펑 터는 Array.prototype.map()일반 JS로 코딩 된 등가물에 비해 느립니다. 100 % 이상의 성능 향상을 얻기 위해 Array.prototype.rotate()내가 시도 할 때 사용한 것과 같은 프로덕션 코드에서 배열을 회전해야하는 경우 다음을 선택할 수 있습니다 .String.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 () 및 splice ()는 실제로 map () 또는 함수 매개 변수를 사용하는 메서드를 사용하는 것보다 느릴 수 있습니다. shift () 및 splice ()가 더 선언적이면 장기적으로 최적화하기가 더 쉽습니다.
진 빈센트

@Jean Vincent 네 맞아요 .. 사실지도가 느려질 수도 있지만 이상적인 회전 논리는 이것이라고 믿습니다. 논리적 접근 방식을 보여 주려고했습니다. for 루프를 사용하면 맵을 쉽게 단순화 할 수 있으며 속도가 크게 향상 될 수 있습니다. 그러나 실제적으로 받아 들여지는 대답은 다른 언어를 염두에두고 개발 된 접근 방식을 가지고 있다는 것입니다. JS에 이상적이지 않습니다 .... 배열 크기가 호출 스택 크기보다 클 때도 실행되지 않습니다. (제 경우와 이번 세션에서는 250891 개의 아이템으로 만 제한되는 것으로 밝혀졌습니다.) 그래서 저기 ..!
Redu

적용 할 수있는 호출 스택 크기에 대해 맞습니다. 이것은 속도보다 더 유효한 인수입니다. 개선 된 들여 쓰기로이 점을 강조하기 위해 답변을 편집하는 것이 좋습니다.
Jean Vincent

2

이 함수는 작은 배열의 경우 허용되는 답변보다 약간 빠르지 만 큰 배열의 경우 훨씬 빠릅니다. 이 함수는 또한 원래 함수의 제한 인 배열 길이보다 큰 임의의 회전 수를 허용합니다.

마지막으로 수락 된 답변은 설명한대로 반대 방향으로 회전합니다.

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

그리고 기능적 동등 물 (성능상의 이점도있는 것 같음) :

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

여기 에서 성능 분석을 확인할 수 있습니다 .


불행히도 작동하지 않고 배열을 크기 0으로 축소하여 첫 번째 실행 후 특히 더 큰 배열에서 다른 올바른 구현보다 훨씬 빠른 이유를 설명합니다. 벤치 마크 테스트에서 테스트 순서를 바꾸고 끝에 길이를 표시하면 문제가 표시됩니다.
Jean Vincent

2

편집하다:: 이봐 그래서 너무 많은 반복이 일어나고 있음이 밝혀졌습니다. 루프도, 분기도 없습니다.

오른쪽 회전의 경우 음수 n으로, 모든 크기 n의 경우 왼쪽 회전의 경우 양수 n으로 작동합니다. Mutation free

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

킥킥 웃음을위한 코드 골프 버전입니다.

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

EDIT1 :: * 분기없는, 돌연변이없는 구현.

그래서 이봐, 내가 필요하지 않은 지점이 있었음이 밝혀졌습니다. 다음은 작동하는 솔루션입니다. 음수 num = 오른쪽 회전 | num | 양수 num = 왼쪽 회전 num

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

방정식 ((n%l) + l) % l은 임의적으로 큰 n 값의 양수와 음수를 정확하게 매핑합니다.

실물

좌우로 회전합니다. 양수로 왼쪽으로 n회전하고 음수로 오른쪽으로 회전 n합니다.

의 음란 큰 입력을위한 작품 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]
   })
}

또는

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

설명:

A의 각 인덱스를 인덱스 오프셋의 값에 매핑합니다. 이 경우

offset = num

이 경우 offset < 0다음 offset + index + positive length of A오프셋 역 가리킬 것이다.

그렇다면 offset > 0 and offset < length of A현재 인덱스를 A의 오프셋 인덱스에 매핑하면됩니다.

그렇지 않으면 오프셋과 길이를 모듈로하여 배열 경계의 오프셋을 매핑합니다.

예를 들어 가지고 offset = 4offset = -4.

offset = -4, 그리고 A = [1,2,3,4,5]각 인덱스, offset + index크기 (또는 것 Math.abs(offset)) 작은합니다.

먼저 음수 n의 인덱스 계산을 설명해 봅시다. A[(((n % A.length) + A.length) % A.length)+0]겁을 먹었습니다. 그러지 마. 그것을 해결하는 데 Repl에서 3 분이 걸렸습니다.

  1. n케이스가이므로 부정적이라는 것을 알고 n < 0있습니다. 숫자가 배열의 범위보다 크면 범위에 n % A.length매핑합니다.
  2. n + A.length그 숫자를 A.length에 추가 하여 n을 정확한 양으로 오프셋하십시오.
  3. n케이스가이므로 부정적이라는 것을 알고 n < 0있습니다. n + A.length그 숫자를 A.length에 추가 하여 n을 정확한 양으로 오프셋하십시오.
  4. 다음으로 모듈로를 사용하여 A의 길이 범위에 매핑합니다. 두 번째 모듈은 계산 결과를 색인 가능한 범위에 매핑하는 데 필요합니다.

    여기에 이미지 설명 입력

  5. 첫 번째 색인 : -4 + 0 = -4. A.length = 5. A.length-4 = 1. A 22 입니다. 인덱스 0을 2로 매핑합니다.[2,... ]

  6. 다음 인덱스, -4 + 1 = -3. 5 + -3 = 2. A 2 는 3입니다. 인덱스 1을 3으로 매핑합니다.[2,3... ]
  7. 기타.

동일한 프로세스가 offset = 4. 때 offset = -4, 그리고 A = [1,2,3,4,5]각 인덱스, offset + index크기가 큰 것.

  1. 4 + 0 = 0. A [0]을 A [4]의 값에 매핑합니다.[5...]
  2. 4 + 1 = 5, 5는 인덱싱 할 때 범위를 벗어 났으므로 A 2 를의 나머지 값인 5 / 50에 매핑 합니다. A 2 = A [0]의 값입니다.[5,1...]
  3. 반복.

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

비 돌연변이 솔루션

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

돌연변이

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

1

회전 목마에서 회전하는 데 사용중인 솔루션을 공유하고 있습니다. 배열 크기가보다 작 으면 중단 될 displayCount수 있지만, 크기가 작을 때 회전을 중지하거나 기본 배열 * displayCount 시간도 연결하는 추가 조건을 추가 할 수 있습니다.

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

카운터를 증가시킨 다음 배열 길이로 나머지 나누기를 가져와야 할 위치를 얻는 것은 어떻습니까?

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

이 외에도 언어 구문이 잘 작동합니다.


2
이것은 어떤 식 으로든 배열을 회전시키지 않습니다.
Jean Vincent

1
사실 (답변에서 언급했듯이)이지만 배열을 회전하지 않아도됩니다.
tgandrews

1
그러나이의 요점은 특정 오프셋에서 배열을 표시하지 않고 제목별로 배열을 회전하는 것입니다. 동일한 루프를 사용하여 새 배열을 다시 빌드하면 기본 Array 메서드를 사용하는 다른 버전보다 훨씬 느립니다. 또한 무한 루프에서 벗어나는 것을 잊었습니다.
Jean Vincent

0

배열이 커지거나 많이 회전하려는 경우 배열 대신 연결 목록을 사용하는 것이 좋습니다.


동의했지만 질문은 배열과 관련이 있으며 더 구체적으로 배열 동사를 확장하는 것입니다.
Jean Vincent

0

@molokoloco 방향으로 회전하도록 구성 할 수있는 기능이 필요했습니다. 앞으로는 true이고 뒤로는 false입니다. 방향, 카운터 및 배열을 가져 와서 적절한 방향으로 증가 된 카운터와 이전, 현재 및 다음 값으로 개체를 출력하는 스 니펫을 만들었습니다. 원래 배열을 수정하지 않습니다.

나는 또한 당신의 스 니펫에 대해 그것을 기록했으며 빠르지는 않지만 당신이 비교하는 것보다 빠릅니다-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)

이것은 어레이를 회전시키지 않습니다. 이 질문에 답하려면 첫 번째 항목이 카운터에있는 배열을 반환해야합니다.
Jean Vincent

0

늦게오고 있지만이 좋은 답변에 추가 할 벽돌이 있습니다. 그런 함수를 코딩하라는 요청을 받았으며 먼저 다음과 같이했습니다.

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

그러나 그것이 클 때 따르는 것보다 덜 효율적으로 보였다 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

이것이 가장 효율적인 방법인지 확실하지 않지만 읽는 방식이 마음에 들며 프로덕션에서 테스트했기 때문에 대부분의 대규모 작업에 충분히 빠릅니다 ...

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

기본적이고 빠르며 작고 의미가 있으며 오래된 엔진에서 작동하며 "curryable"입니다.

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

0

** 최신 버전의 JS를 사용하여 쉽게 빌드 할 수 있습니다. **

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

여기 이동 : 회전 수 , 난수를 전달할 수 있는 배열

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

0

ArrayJS에는 배열을 매우 쉽게 회전하는 데 사용할 수있는 메서드가 내장되어 있으며 이러한 메서드는 본질적 으로 불변 합니다.

  • push: 배열의 끝에 항목을 삽입합니다.
  • pop: 배열 끝에서 항목을 제거합니다.
  • unshift: 항목을 배열의 시작 부분에 삽입합니다.
  • shift: 배열의 시작 부분에서 항목을 제거합니다.

아래 솔루션 ( ES6)은 두 개의 인수 (배열을 회전해야 함)와 n (배열을 회전해야하는 횟수)을 취합니다.

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

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

Array.prototype에 추가 할 수 있으며 애플리케이션 전체에서 사용할 수 있습니다.

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

for 루프 사용. 단계는 다음과 같습니다.

  1. 배열의 첫 번째 요소를 임시 변수로 저장합니다.
  2. 그런 다음 왼쪽에서 오른쪽으로 바꿉니다.
  3. 그런 다음 배열의 마지막 요소에 임시 변수를 할당합니다.
  4. 회전 수에 대해이 단계를 반복합니다.

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



0

다음을 사용하십시오-

arr=[1,2,3,4,5]  
let arrs=[]
arrs=arr.slice(d%arr.length).concat(arr.slice(0,d%arr.length))

여기서, d회전 싫군이다
베스트 솔루션으로 밀어 터지는 브 루트 포스 기법 적용 불필요 O를 1 시간 복잡도

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