Rotate the elements in an array in JavaScript

后端 未结 30 1195
走了就别回头了
走了就别回头了 2020-11-22 10:55

I was wondering what was the most efficient way to rotate a JavaScript array.

I came up with this solution, where a positive n rotates the array to the

相关标签:
30条回答
  • 2020-11-22 10:59

    If your array is going to be large and/or you are going to rotate a lot, you might want to consider using a linked list instead of an array.

    0 讨论(0)
  • 2020-11-22 11:02

    I am not sure if this is the most efficient way but I like the way it reads, it's fast enough for most large tasks as I have tested it on production...

    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 讨论(0)
  • 2020-11-22 11:02

    Using for loop. Here are the steps

    1. Store first element of array as temp variable.
    2. Then swap from left to right.
    3. Then assign temp variable to last element of array.
    4. Repeat these steps for number of rotations.

    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 讨论(0)
  • 2020-11-22 11:03

    Type-safe, generic version which mutates the array:

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

    In the comments, Jean raised the issue that the code doesn't support overloading of push() and splice(). I don't think this is really useful (see comments), but a quick solution (somewhat of a hack, though) would be to replace the line

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

    with this one:

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

    Using unshift() instead of push() is nearly twice as fast in Opera 10, whereas the differences in FF were negligible; the code:

    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;
        };
    })();
    
    0 讨论(0)
  • 2020-11-22 11:03

    EDIT:: Hey so turns out there's too much iteration happening. No loops, no branching.

    Still works with negative n for right rotation and positive n for left rotation for any size 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))
    }
    

    Here's the code golf version for giggles

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

    EDIT1::* Branchless, mutationless implementation.

    So hey, turns out I had a branch where I didn't need it. Here is a working solution. negative num = right rotate by |num| positive num = left rotate by num

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

    The equation ((n%l) + l) % l maps exactly positive and negative numbers of any arbitrarily large values of n

    ORIGINAL

    Rotate left and right. Rotate left with positive n, rotate right with negative n.

    Works for obscenely large inputs of n.

    No mutation mode. Too much mutation in these answers.

    Also, fewer operations than most answers. No pop, no push, no splice, no shift.

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

    or

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

    Explanation:

    map each index of A to the value at index offset. In this case

    offset = num
    

    if the offset < 0 then offset + index + positive length of A will point to the inverse offset.

    if offset > 0 and offset < length of A then simply map the current index to the offset index of A.

    Otherwise, modulo the offset and the length to map the offset in the bounds of the array.

    Take for instance offset = 4 and offset = -4.

    When offset = -4, and A = [1,2,3,4,5], for each index, offset + index will make the magnitude (or Math.abs(offset)) smaller.

    Let's explain the calculation for the index of negative n first. A[(((n % A.length) + A.length) % A.length)+0] and been intimidated. Don't be. It took me 3 minutes in a Repl to work it out.

    1. We know n is negative because the case is n < 0. If the number is larger than the range of the Array, n % A.length will map it into the range.
    2. n + A.length add that number to A.length to offset n the correct amount.
    3. We know n is negative because the case is n < 0. n + A.length add that number to A.length to offset n the correct amount.
    4. Next Map it to the range of the length of A using modulo. The second modulous is necessary to map the result of the calculation into an indexable range

    5. First index: -4 + 0 = -4. A.length = 5. A.length - 4 = 1. A2 is 2. Map index 0 to 2. [2,... ]

    6. Next index, -4 + 1 = -3. 5 + -3 = 2. A2 is 3. Map index 1 to 3. [2,3... ]
    7. Etc.

    The same process applies to offset = 4. When offset = -4, and A = [1,2,3,4,5], for each index, offset + index will make the magnitude bigger.

    1. 4 + 0 = 0. Map A[0] to the value at A[4]. [5...]
    2. 4 + 1 = 5, 5 is out of bounds when indexing, so map A2 to the value at the remainder of 5 / 5, which is 0. A2 = value at A[0]. [5,1...]
    3. repeat.
    0 讨论(0)
  • 2020-11-22 11:04

    Easy solution with slice and destructuring:

    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]

    0 讨论(0)
提交回复
热议问题