hansmaad
hansmaad

Reputation: 18905

How to resize an array

Is there an Array method that can replace the following function (Something like a.splice(some, tricky, arguments))?

function resize(arr, newSize, defaultValue) {
  if (newSize > arr.length)
    while(newSize > arr.length)
      arr.push(defaultValue);
  else
    arr.length = newSize;
}

If not, is there a better / nicer / shorter / implementation? The function should append a default value if the array should grow and remove values on shrink.

Upvotes: 17

Views: 52370

Answers (8)

S4PJINC
S4PJINC

Reputation: 1

Suggestion:

const expectedLength = 500;
myArray = myArray.filter((element, index) => {
     if (index < expectedLength) {
         return element;
     }
});

Upvotes: 0

MSmedberg
MSmedberg

Reputation: 481

It is much more efficient to resize an array once, than it is to do a large number of .pushes. This can be accomplished by the setter feature on Array.prototype.length:

function resize(arr, newLength, defaultValue) {
  const oldLength = arr.length;
  arr.length = newLength;
  if (newLength > oldLength && typeof(defaultValue) !== 'undefined') {
    for (let i = oldLength; i < newLength; i++) {
      arr[i] = defaultValue;
      // Note: this will create many references to the same object, which
      // may not be what you want. See other answers to this question for 
      // ways to prevent this.
    }
  }
}

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 192006

This solution will only work for new browsers (except IE) due to Array.prototype.fill (see Browser compatibility at the bottom of the linked page) or using a polyfill.

function resize(arr, newSize, defaultValue) {
    var originLength = arr.length; // cache original length

    arr.length = newSize; // resize array to newSize

    (newSize > originLength) && arr.fill(defaultValue, originLength); // Use Array.prototype.fill to insert defaultValue from originLength to the new length
}

Upvotes: 2

James Brierley
James Brierley

Reputation: 4670

In terms of elegance, I would say you could trim down your original solution to:

function resize(arr, newSize, defaultValue) {
    while(newSize > arr.length)
        arr.push(defaultValue);
    arr.length = newSize;
}

Or use prototype:

Array.prototype.resize = function(newSize, defaultValue) {
    while(newSize > this.length)
        this.push(defaultValue);
    this.length = newSize;
}

Edit: ES2016 approach:

function resize(arr, newSize, defaultValue) {
    return [ ...arr, ...Array(Math.max(newSize - arr.length, 0)).fill(defaultValue)];
}

Upvotes: 23

Reza
Reza

Reputation: 3919

You can use the following code for resizing arrays:

// resizing function for arrays
Array.prototype.resize = function( newSize, defaultValue )
{
  while( newSize > this.length )
  {
    typeof( defaultValue ) === "object" ? this.push( Object.create( defaultValue ) ) : this.push( defaultValue );
  }
  this.length = newSize;
}

I checked the type of defaultValue because if it is an object and you just push it to the new elements, you will end up with an array which new elements point to the same object. That means if you change a property of that object in one of your array's element, all others will also change. But if your defaultValue is a primitive, you can safely push it to the new elements.

Upvotes: 2

For one-liner-lovers

If you are one-liner-lover like me, what you're asking would be what my resize_array_right does.


const resize_array_left = (array, length, fill_with) => (new Array(length)).fill(fill_with).concat(array).slice(-length);
// Pads left when expanding an array.
// Put left elements first to be removed when shrinking an array.

const resize_array_right = (array, length, fill_with) => array.concat((new Array(length)).fill(fill_with)).slice(0, length);
// Pads right when expanding an array.
// Put right elements first to be removed when shrinking an array.

You can find it out in NPM, as resize-array.


Browser compatibility

  • Chrome: 45 and above.
  • Firefox: 31 and above.
  • Internet Explorer: ✘.
  • Edge: ✔.

Upvotes: 3

crush
crush

Reputation: 17023

Expanding on James solution:

Array.prototype.resize = function(newSize, defaultValue) {
    while(newSize > this.length)
        this.push(defaultValue);
    this.length = newSize;
}

If you want to get even more efficient, you could do browser detection for Array.prototype.fill and use that instead of the while loop.

if (Array.prototype.fill) {
    Array.prototype.resize = function (size, defaultValue) {
        var len = this.length;

        this.length = size;

        if (this.length - len > 0)
            this.fill(defaultValue, len);
    };
} else {
    Array.prototype.resize = function (size, defaultValue) {
        while (size > this.length)
            this.push(defaultValue);

        this.length = size;
    };
}

If someone has included a polyfill for Array.prototype.fill, then you want them to use your non-fill version instead. A polyfill would cause the fill method to be slower than the non-fill version.

This StackOverflow Q&A deals with how to detect if a function is natively implemented. You could work that into the initial condition, but that is just additional speed lost.

I'd probably only use this solution if you could ensure that no Array.prototype.fill would exist.

Upvotes: 1

wayofthefuture
wayofthefuture

Reputation: 9415

function resize(arr, size, defval) {
    while (arr.length > size) { arr.pop(); }
    while (arr.length < size) { arr.push(defval); }
}

I think this would be more efficient though:

function resize(arr, size, defval) {
    var delta = arr.length - size;

    while (delta-- > 0) { arr.pop(); }
    while (delta++ < 0) { arr.push(defval); }
}

And while not as elegant, this would probably me the most efficient:

function resize(arr, size, defval) {
    var delta = arr.length - size;

    if (delta > 0) {
        arr.length = size;
    }
    else {
        while (delta++ < 0) { arr.push(defval); }
    }
}

Upvotes: 7

Related Questions