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!

Dany D
  • 1,189
  • 2
  • 18
  • 44

8 Answers8

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

Community
  • 1
  • 1
Jeff Noel
  • 7,500
  • 4
  • 40
  • 66
  • What's wrong with my answer? – Jeff Noel Aug 13 '13 at 14:36
  • I'm not the downvoter but I *suspect* it's to do with `for .. in` which (at least in my experience) is discouraged. – cdhowie Aug 13 '13 at 14:45
  • @cdhowie You are right. I just realized the `for ... in` causes a huge performance impact! – Jeff Noel Aug 13 '13 at 14:48
  • Since you have removed that I can now upvote with a clear conscience. :) – cdhowie Aug 13 '13 at 14:49
  • 1
    @cdhowie I also included a performance comparison. This was actualy slowing performances of the script by **98%** over my old `for ... in`. – Jeff Noel Aug 13 '13 at 14:50
  • 1
    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()`. – cdhowie Aug 13 '13 at 14:52
  • Why did you add the `break;`? – Ian Aug 13 '13 at 15:01
  • @Ian Thank you for the question, it reminded me I forgot to add a comment at this part of the code: the `break;` can be removed if you want to remove all the matching elements. If you do so, you also need to **invert the loop** so that `i == crossesPos.length; i>=0; i--` otherwise splicing will affect the index positions of your array elements. – Jeff Noel Aug 13 '13 at 15:05
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)

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • Would the downvoter care to explain their vote? This code is tested and working. – cdhowie Aug 13 '13 at 14:40
  • 1
    It seems like someone's spamming downvotes for no reasons. I did not actualy know about `$.grep()`, so thank you for your answer! – Jeff Noel Aug 13 '13 at 14:43
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);
    });
kayen
  • 4,838
  • 3
  • 19
  • 20
  • The test should be `(n[0] != x || n[1] != y)`. – cdhowie Aug 13 '13 at 14:32
  • 1
    Read again - the ask is to remove the array `[317, 193]`. – kayen Aug 13 '13 at 14:34
  • 1
    Yes, and this code would also remove `[317, 0]` and `[0, 193]`. – cdhowie Aug 13 '13 at 14:37
  • 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. – kayen Aug 13 '13 at 14:39
  • Awww...why the down vote? – kayen Aug 13 '13 at 14:40
  • 1
    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 `&&`. – cdhowie Aug 13 '13 at 14:41
  • @cdhowie - You're absolutely correct! Sorry, my bad! Bad bad robot - no doughnut! – kayen Aug 13 '13 at 14:44
  • FWIW, it's generally easier to negate the whole thing than try to distribute the negation through the condition. Example: `!(n[0] == x && n[1] == y)` (distributing the negation means flipping `==` to `!=` *and* `&&` to `||`). Or you can use the third "invert" argument to `grep()` as I chose to do in my answer. All are equivalent. – cdhowie Aug 13 '13 at 14:47
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);
Tricky12
  • 6,752
  • 1
  • 27
  • 35
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);
    }
}
Nicolae Olariu
  • 2,487
  • 2
  • 18
  • 30
  • 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 – Ian Aug 13 '13 at 14:50
  • Ahh, oops, you're right, a for/while loop in reverse will do the trick. – Nicolae Olariu Aug 13 '13 at 15:18
  • 1
    Plunk updated to use for loop in reverse. – Nicolae Olariu Aug 13 '13 at 15:29
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;
deitch
  • 14,019
  • 14
  • 68
  • 96
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]

Barnabé Monnot
  • 1,163
  • 1
  • 9
  • 19
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 );

//
public override
  • 974
  • 8
  • 17