Search code examples
javascriptarraystyped-arrays

Shift values of typed Javascript array?


I have a typed Javascript Array (Float32Array) and I want to insert one value at a time at the beginning of the array and move all other values by one position (the last value gets deleted).

Here is an example:
Let's say I have initialized the array with a size of 3. The array currently holds the values [2.0, 7.2, 4.5] and I want to insert the number 8.1. The result should be [8.1, 2.0, 7.2].

If I would use a normal JS array this would no problem – I could use functions like .unshift() or .pop(), but these methods don't work on typed JS arrays, because they change the size of the array.

Is there a way to efficiently implement this using a typed Javascript Array?


Solution

  • You can "unshift" a value on a fixed array by shifting all the value at the end to the right by 1. After you have done that, you just replace the head with the value parameter. Traditional for-loops are going to be the most efficient way to do this.

    The key here is to loop backwards, filling the last slot with its neighbor to the left.

    Works for all the indexed collections:

    • Array
    • Int8Array, Uint8Array, Uint8ClampedArray
    • Int16Array, Uint16Array
    • Int32Array, Uint32Array
    • Float32Array, Float64Array
    • BigInt64Array, BigUint64Array

    As you already know, TypedArray objects do not have all the methods available on standard Array objects.

    const shiftRight = (collection, value) => {
      for (let i = collection.length - 1; i > 0; i--) {
        collection[i] = collection[i - 1]; // Shift right
      }
      collection[0] = value; // Place new value at head
      return collection;
    }
    
    const shiftLeft = (collection, value) => {
      for (let i = 0; i < collection.length - 1; i++) {
        collection[i] = collection[i + 1]; // Shift left
      }
      collection[collection.length - 1] = value; // Place new value at tail
      return collection;
    }
    
    const float32 = new Float32Array([2.0, 7.2, 4.5]);
    
    console.log(shiftRight(float32, 8.1)); // >>> [ 8.1, 2.0, 7.2 ]
    console.log(shiftLeft(float32, 4.5));  // <<< [ 2.0, 7.2, 4.5 ]
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    Usually you do not want to pollute the prototype with additional methods, but if you are calling this everywhere, you could devise the following:

    shiftRight=(c,v)=>{for(i=c.length-1;i>0;i--)c[i]=c[i-1];c[0]=v;return c}
    shiftLeft=(c,v)=>{for(i=0;i<c.length-1;i++)c[i]=c[i+1];c[c.length-1]=v;return c}
    
    /* Needs to be added to all the various indexed collections */
    if (Float32Array.prototype.shiftRight === undefined) {
      Float32Array.prototype.shiftRight = function(value) {
        return shiftRight(this, value);
      };
    }
    if (Float32Array.prototype.shiftLeft === undefined) {  
      Float32Array.prototype.shiftLeft = function(value) {
        return shiftLeft(this, value);
      };
    }
    
    const float32 = new Float32Array([2.0, 7.2, 4.5]);
    
    console.log([...float32.shiftRight(8.1).values()]); // >>> [ 8.1, 2.0, 7.2 ]
    console.log([...float32.shiftLeft(4.5).values()]);  // <<< [ 2.0, 7.2, 4.5 ]
    .as-console-wrapper { top: 0; max-height: 100% !important; }