19

I would like to cache some data in javascript, but the cache should be limited to 10 elements for example.

I can place the objects in javascript array, but what is the best way to keep the array limited to 10 elements?

Example:

function getData(dataId) { return new NextDataObject(dataId); }

var array = new Array();

array.push(getData(0));
array.push(getData(1));
(...)
array.push(getData(10)); // this should result in dropping "oldest" data, so getData(0) should be removed from the array, so that in array there are only 10 objects at maximum

Should such mechanism be written manually (using splice() for example?) or are there better ways to achieve such "cache" structure in javascript?

BTW: in this particular situation I'm using angular.

1
  • Must it be an array or could it be an object? Commented Jun 24, 2015 at 9:58

9 Answers 9

23

Override the push function of your caching array.

var array = new Array()
array.push = function (){
    if (this.length >= 10) {
        this.shift();
    }
    return Array.prototype.push.apply(this,arguments);
}

Plunker


To make this more reusable I created a method which returns new instance of such array (basing on above code).

function getArrayWithLimitedLength(length) {
    var array = new Array();

    array.push = function () {
        if (this.length >= length) {
            this.shift();
        }
        return Array.prototype.push.apply(this,arguments);
    }

    return array;

}

var array = getArrayWithLimitedLength(10);
Sign up to request clarification or add additional context in comments.

7 Comments

I don't think this is the best way, because if he has another situation where the array CAN exceed the length of 10, push() would still limit it to 10..
@Guinn Pretty decent solution. And in "another situation where the array CAN exceed" one would just use clean array instance with original push from prototype.
@Michael You want to use shift not unshift.
Ah it's only for this specific array, I must've missed that. My bad
This solution is great, because it does not modify the prototype of Array object, and is simple
|
11

To remove first element from array use shift:

if (arr.length > 10) {
    arr.shift(); // removes the first element from an array 
}

2 Comments

same second answer xD
@Tushar, I guess your second answer is incorrect, it is actually the "shift" which helps us to remove the first element.
9

How about this object?

function Cache(maxLength) {
  this.values = [];

  this.store = function(data) {
    if(this.values.length >= maxLength) {
      this.getLast();
    }
    return this.values.push(data);
  }

  this.getLast = function() {
    return this.values.splice(0,1)[0];
  }
}

cache = new Cache(3);
// => Cache {values: Array[0]}
cache.store(1)
// => 1
cache.store(2)
// =>2
cache.store(3)
// => 3
cache.store(4)
// =>3
cache.values
// => [2, 3, 4]
cache.getLast()
// => 2
cache.values
[3, 4]

4 Comments

Custom data structure might be the best solution in this case. Easily testable and very flexible.
Thank you. I don't believe it's good practice to alter the Array prototype for this kind of functionality. (What if some other library does too?) I also believe it's good practice to create nice, contained, OO units of code. They are then adaptable, reusable, and if you need to change the functionality they have an interface you can conform to.
Nice solution (which I was also thinking about), but overriding method for single instance of array, as propsed by @Michael is much better, because it is simple.
It is indeed elegant, with a small footprint. If your codebase is small and simple and will stay that way it is an excellent solution. If you foresee the need for multiple caches or more complex cache behaviour in future, it would need to be augmented.
3

You could create new method in Array.prototype to mimic your needs.

Array.prototype.push_with_limit = function(element, limit){
  var limit = limit || 10;
  var length = this.length;
  if( length == limit ){
    this.shift();
  }
  this.push(element);
}

var arr = []
arr.push_with_limit(4); // [4]
arr.push_with_limit(9); // [4, 9]
....
// 11th element
arr.push_with_limit(3); // [9, ..., 3]  10 elements

2 Comments

This gets the job done without much code. I tend to shy away from changing the prototype of fundamental JS objects though... Such changes could interact with other code which does the same or similar things. Also, if I wanted to add more complexity later I'd either have to pollute Array's prototype even more, or change the way this functionality is used all over the codebase. That and I'm a total chicken. Bk-arrrrk.
OK, but I don't like this because it changes the prototype of Array object
2

Simple fixed length queue:

Array.prototype.qpush = function( vals, fixed ) {
    if (arguments.length) {
        if (Array.isArray(vals)) {
            for (var v of vals) {
                this.push(v);
            }
        } else {
            this.push(vals);
        }
        var _f = (typeof this.fixed != undefined) ? this.fixed : 0;
        if (typeof fixed != undefined) {
            _f = (Number(fixed)===fixed && fixed%1===0 ) ? fixed : _f;
        }
        this.fixed = _f;
        if (this.fixed>0) this.splice(0, this.length - _f);        
    }
}

var q = new Array();
q.push(0);
q.qpush( [1, 2, 3], 10 );
q.qpush( [4] );
q.qpush( 5 );
q.qpush( [6, 7, 8, 9, 10, {k:"object"} ] );
console.log(q);

1 Comment

OK, but I don't like this because it changes the prototype of Array object
1
if(array.length == 10) {

    array.splice(0, 1);
    // this will delete first element in array
}

Comments

1

If you do a check whether the array has reached 10 entries with array.length, just remove the first element before pushing a new element. This can be done several ways as Tushar states, array.shift() would be the fastest, but you can indeed use array.splice() aswell.

It would look like this:

if(array.length > 10) {
    array.shift();
    array.push(getData(10));
}

On a side note, instead of using var array = new Array() I suggest you simply use var array = [];. This is because the new keyword in Javascript sometimes has bad side effects. If you for example want to create an array with 1 element being a digit, and you use var arr = new Array(12);, an array with 12 undefined elements will be created. Whereas var arr = [12]; will create an array with 1 element, the digit 12.

But I guess that's a minor thing to consider..

4 Comments

This should be posted as Comment to question not an answer.
Uh, why? I answered your question didn't I? If you consider a textual answer instead of code you can copy paste not appropriate, I updated with the code you can copy paste ~_~
I gave "plus one", but please note that I did not ask how I should initialise Array. I asked how should I limit and you started your answer with the "lesson" how to limit array... you could place this in the end of the answer or as BTW, but you should not start your answer with that. BTW: code fragment does not matter. You could edit your answer according to this and it would be fine.
Well then, there you go
1

You could use an object instead...

var obj = {}; //your cache object
obj[window.performance.now()] = getData(val); //add value, index by microsecond timestamp
if(Object.keys(obj).length > 10){ // then if the length ever gets bigger than 10..
 var array = Object.keys(obj).sort(); //sort the properties by microsecond asc
 delete obj[array[0]]; //delete the oldest one   
}

Here is a jsFiddle example showing how it works: https://jsfiddle.net/uhkvk4mw/

1 Comment

Thanks for the code, but this is way to complicated for so simple task IMHO.
0

just check if the length is reached then pop it

if(arr.length > someNumber){
arr.pop(); // pop() will remove the last element
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.