Search code examples
javascriptarraysmethodsprototypesplice

Is there a simple way to quickly pass the entire contents of a given array into an `Array.prototype` method variable without using parameters?


context

I created an array docket to keep track of coordinates as the user clicks on a canvas space. During the main program loop the array is to be scanned by a draw function so that selected pixels can be seen. Originally, inside of my event listener, I was using the push( ) method but then I realized I wanted a way to sort of toggle the pixels.

code description

So I added a method poke( ) to Array.prototype, as seen below, which allows me to push the whole docket array into a local array param.array and assign the trigger coordinate to a local variable param.entry. entry is then pushed into array and array is processed by the main poke( ) loop to ensure there are no duplicate values. If a match is found, both elements are annihilated and param.array is returned to the top, ultimately shrinking docket by 1; If no matches are found then no elements are annihilated and param.array is returned to the top, ultimately expanding docket by 1.

main issue: example 1

Anyway, as the method is currently written, it must be called thusly:

docket.poke( docket, e.key ); Note: for simplicity I have used keyboard key values.

Array.prototype.poke = function( a, b ) {
  var bool = {  },  i = {  },  param = {  };
  param.array = a;  param.entry = b;    
  //
  param.array.push( param.entry );
  i.len = param.array.length;
  i.end = i.len - 1;
  //
  for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
    bool.match = param.array[ i.cur ] == param.array[ i.end ];
    bool.nSelf = !( i.cur == i.end );
    //
    if ( bool.match && bool.nSelf ) {
      param.array.splice( i.end, 1 );
      param.array.splice( i.cur, 1 );
      //
      i.end -= 2;
      i.len -= 2;
    }
  }
  //
  return param.array;
}

This seems a little redundant, but it offers two critical advantages. First to readability and aesthetic. Being able to visibly pass off the contents of docket to a local array for processing and then visibly return the results to the top I think is very helpful to comprehension. Second, both this example and the next use a sort of confusing truth test to filter out false positives on duplicate value detection. This example doesn't have too though. It could easily be rewritten to compare each element in param.array to param.entry using a tight, no nonsense for loop.

main issue: example 2

docket.poke( e.key ); is the less redundant and more desired approach. This is my code.

Array.prototype.poke = function( a ) {
  var bool = {  },  entry = a,  i = {  };
  //
  this.push( entry );
  i.len = this.length;
  i.end = i.len - 1;
  //
  for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
    bool.match = this[ i.cur ] == this[ i.end ];
    bool.nSelf = !( i.cur == i.end );
    //
    if ( bool.match && bool.nSelf ) {
      this.splice( i.end, 1 );
      this.splice( i.cur, 1 );
      //
      i.end -= 2;
      i.len -= 2;
    }
  }
}

As you can see, this eliminates the the redundancy in the call, but it sacrifices some readability of the method and more importantly the opportunity to really slim up the code using the simple comparison I mentioned above.

So now I'm wondering if there is some less than obvious way that I've missed which will allow me to pass the full contents of my array to a local variable without having to first pass them in as a parameter of its own method.

Any ideas?


Solution

  • There is no reason to define the method on the prototype if you are going to pass the array as an argument. A plain function would be just fine for that.

    The second version of your code has indeed the advantage that you can apply the method to a given array instead of passing the array to a function.

    The code could however be simplified if:

    • You would only add the element after you have determined it does not yet occur in the array
    • You would use indexOf:

    Array.prototype.toggle = function(value) {
        var index = this.indexOf(value);
        if (index > -1) {
            this.splice(index, 1);
        } else {
            this.push(value);
        }
    }
    
    var a = [4,2,5,8];
    a.toggle(2);
    console.log(a.join());
    a.toggle(2);
    console.log(a.join());

    NB: I personally find the name toggle more telling than poke.

    Consider also the power of a Set: it will find an existing member in constant time (while an array implementation needs linear time), and will also be able to remove it in constant time. So if you are open to using something else than an array for this, go for a Set.

    Set.prototype.toggle = function(value) {
        if (!this.delete(value)) this.add(value);
    }
    
    var a = new Set([4,2,5,8]);
    a.toggle(2);
    console.log([...a].join());
    a.toggle(2);
    console.log([...a].join());