2

I have the following array, which holds other arrays (they are in fact coordinates of a html5 canvas).

var crossesPos = [[317, 193], [110, 334], [390, 347], [281, 222], [307, 384], [329, 366], [230, 104], [276, 156], [173, 330], [227, 100], [397, 261], [341, 389], [233, 223], [261, 350], [267, 286]]

Lest say:

x = 317;
y = 193;

In the following function, how can I remove the array [317,193] from crossesPos?

function checkForCrosses(x,y){
    var position;
    jQuery.each(crossesPos, function(){
        position = this;
        if(x == position[0] && y == position[1]){
            // how do I remove the array [317,193] from crossesPos?
        }
    });
}

Ty!

0

8 Answers 8

5

Use the following code to splice the exact coordinates from the array. Put this code in your function. This is more efficient than JQuery code.

Pure JavaScript

for(var i=0; i<crossesPos.length; i++)
{
    if(crossesPos[i][0] === x)
        if(crossesPos[i][1] === y)
        {
            crossesPos.splice(i,1);
            break;
        }
}

Important note: If you want to delete all the matching elements within the array (and not only one), you must edit the code, deleting the break; condition and inverting the loop:

for(var i=crossesPos.length-1; i>=0; i--)
{
     if(crossesPos[i][0] === x)
        if(crossesPos[i][1] === y) crossesPos.splice(i,1);          
}

Working demo (with sexy output)


Performance comparison (test it yourself!)

This is (at the time of this post) the most efficient and performant way to execute your needs, since the closest results are still ~90% slower than my answer.

Performance comparison

Sign up to request clarification or add additional context in comments.

8 Comments

What's wrong with my answer?
I'm not the downvoter but I suspect it's to do with for .. in which (at least in my experience) is discouraged.
@cdhowie You are right. I just realized the for ... in causes a huge performance impact!
@cdhowie I also included a performance comparison. This was actualy slowing performances of the script by 98% over my old for ... in.
Yup, grep is not fast. Only reason I chose that is because it's a solution baked in to jQuery and is easy to understand. But for performance you can't beat array.splice().
|
3

The "jQuery way" to do this would be to use jQuery.grep():

var crossesPos = [[317, 193], [110, 334], [390, 347], [281, 222], [307, 384],
                  [329, 366], [230, 104], [276, 156], [173, 330], [227, 100],
                  [397, 261], [341, 389], [233, 223], [261, 350], [267, 286]];

crossesPos = jQuery.grep(crossesPos,
                         function(e) { return e[0] === 317 && e[1] === 193; },
                         true);

(See it run)

2 Comments

Would the downvoter care to explain their vote? This code is tested and working.
It seems like someone's spamming downvotes for no reasons. I did not actualy know about $.grep(), so thank you for your answer!
3

Use jQuery's grep utility function:

var x = 317,
    y = 193,
    newPos = $.grep(crossesPos, function(n, i){
          return (n[0] != x || n[1] != y);
    });

8 Comments

The test should be (n[0] != x || n[1] != y).
Read again - the ask is to remove the array [317, 193].
Yes, and this code would also remove [317, 0] and [0, 193].
Err, I think you're confused. It does not return if x is 317 and y is 193 in the same array i.e. [317, 193]. It will return for everything else, which is what is required.
For [317, 0] the condition is (317 != 317 && 0 != 193) which is (true && false) which is false, which means that [317, 0] would be incorrectly removed. The function must return false only for [317, 193]. This is why you want || instead of &&.
|
2

You should use the .splice() function.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

crossesPos.splice(index,1);

Comments

2

I think you need to use splice() to delete the item found. This is a working plunk. Open up your console to see the modified array.

Code to add to your function:

function checkForCrosses(x, y){
    var position,
        length = crossesPos.length;

    for (var i = length; i > 0; i--) {
      position = crossesPos[i - 1];

      if (x == position[0] && y == position[1])
        crossesPos.splice(i - 1, 1);
    }
}

3 Comments

While this works, it doesn't do what you expect. The .each() loop iterates over the original array's length. Modifying it as you iterate doesn't change that, so the loop iterates over indexes that don't exist anymore. And since you're encouraging the use of this instead of the second parameter of the callback, you're checking window's properties. If you're going to remove elements (instead of returning a new array of the accepted elements), you should use a normal for/while loop and iterate backwards
Ahh, oops, you're right, a for/while loop in reverse will do the trick.
Plunk updated to use for loop in reverse.
1

http://underscorejs.org or http://lodash.com can make this hugely easier, but if you insiste on doing it straight:

var pos = -1;
jQuery.each(crossesPos,function(idx,item) {
  if (x===item[0] && y===item[1]) {
    pos = idx;
  }
});
crossesPos.splice(idx,1);

Or more simply

var ary = [];
jQuery.each(crossesPos,function(idx,item) {
  if (x!==item[0] || y!==item[1]) {
    ary.push(item);
  }
});
crossesPos = ary;

Comments

1

Using filter

function checkForCrosses(x,y){
    function myFilter(element, index, array) {
        return (element[0] != x || element[1] != y);
    }
    crossesPos = crossesPos.filter(myFilter);
}

Then crossesPos holds the array without [317, 193]

3 Comments

Your condition isn't right, someone already pointed out it should be element[0] != x || element[1] != y if you're going to use the "not equals" approach
This code would also remove [317, 0] and [0, 193] if they were in crossesPos.
Oh yes that's right, I need to review my boolean algebra :)
1
//
//
//  here's some common Array operations you might find usefull and familiar: 
//   
// .each( callback, boolFlgFromLast )
//   # iterate an array runing given callback-fn for every array item, 
//   # pass it curent array key and value, respectively, 
//   # set context of the callback to host array, 
//   # if boolFlgFromLast argument is ( === )true, iterate from last element, 
//   # break iteration if callback returns ( === )false, 
//   # return iterated array
//
// .not( callback ) 
//   # remove items for which callback returns ( === )true
//   # keep others
//   # return host array
//  
// .keep( callback )
//   # keep items for which callback returns ( === )true
//   # remove others
//   # return host array
//
// .desparse()
//   # 'desparse' host array in place
//   # return host array
// 
//
//    var
//       a = [ 
//               [317, 193], 
//               [110, 334], 
//               [390, 347], 
//            ];
//    
//  
//    a
//     .each( function ( k, v ) { console.log('['+ k +'] -> '+ v ); } )
//     .not(  function ( k, v ) { console.log(' * '); return ( v[0] == 317 ) && ( v[1] == 193 ); } )
//     .each( function ( k, v ) { console.log('['+ k +'] -> '+ v ); } )
//     .keep( function ( k, v ) { console.log(' * '); return Math.random() > .1; } )
//     .each( function ( k, v ) { console.log('['+ k +'] -> '+ v + ', [ this === a ] -> '+ ( this === a )  ); } );
//       
//     // make sparse array
//     a[5] = [0,0];
//     console.log('sparse array: ', a);
//     
//     a
//     .desparse()
//     .each( function ( k, v ) { console.log('['+ k +'] -> '+ v ); } )
//       
//
//
;( function( _a ) { 

    var
        t  = !0, 
        f  = !t;

    _a.each = function ( fn ) { 

                    var
                       len = this.length, 
                       i   = 0;

                    for( ; i < len ; i++ ) { 
                       if ( fn.call( this, i, this[i] ) === f ) break;
                    }

                    return this;

    };

    overload( 'each', _a, function ( fn, flgIterateBackwards ) {

                              if ( flgIterateBackwards === t ) {

                                  var
                                     i = this.length - 1;

                                   for ( ; i >= 0 ; i-- ) { 
                                       if ( fn.call( this, i, this[i] ) === f ) break;
                                   }

                                   return this;

                              } else {

                                  return this.each( fn );

                              }
                          } 
            );

    _a.not = function ( callback ) { 
                  return this.each( function ( k, v ) { 
                                      ( callback.call( this, k, v ) === t ) && this.splice( k, 1 );
                                    }, t );
             };

    _a.keep = function ( callback ) { 
                  return this.each( function ( k, v ) { 
                                       ( callback.call( this, k, v ) === t ) || this.splice( k, 1 );
                                    }, t );
              };


    _a.desparse = function () { 
        return this.not( function ( k, v ) { return k in this === f; } );
    };


    // helper fn-s

    function overload ( fn, obj,  newfn ) { 

       return ( function ( origfn ) { 

            obj[fn] = function () {

                        var
                            args = _a.slice.call( arguments );

                        if ( newfn.length == arguments.length ) {

                            return newfn.apply( this, args )

                        } else if ( isfn ( origfn ) ) {

                            return origfn.apply( this, args )

                        } else {
                            // ignore non method props
                        }

                      };

        } )( obj[fn] );
     }  

    function isfn ( o ) { 
       return ( typeof o === 'function' ) && 
              ( Object.prototype.toString.call( o ) === '[object Function]' );
    }


} )( Array.prototype );

//

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.