Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Page 1 of 2 12 LastLast
Results 1 to 15 of 24
  1. #1
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,133
    Thanks
    38
    Thanked 504 Times in 498 Posts

    Question filter() function question

    I'm trying to understand the filter function and have run into a problem with my first test.

    Why does the function seem to add 1 to each element of the original array
    but returns one less element in the new array?

    Code:
    <!DOCTYPE HTML>
    <html>
    <head>
    <title> Untitled </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript">
    
    function addOne(element, index, array) { return (element++); }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    var Barr = Aarr.filter(addOne);
    alert('Orig: \n'+Aarr+'\n\nadd 1: \n'+Barr);
    
    /*
    // From: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter
    function isBigEnough(element, index, array) { return (element >= 10); }
    var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
    alert(filtered);    // filtered is [12, 130, 44] 
    */
    
    </script>
    
    </head>
    <body>
    
    </body>
    </html>
    The commented out test (isButEnough) seems to work as advertised.

  • #2
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Ready to kick yourself?

    Doing return element++; means you return the ORIGINAL VALUE of the value of the element.

    That's what POST-INCREMENT means. "Get the value before the increment as the value of the expression, *then* do the increment."

    Because you are incrementing a LOCAL COPY of element, that effectively means you are just throwing away the incremented value;

    You will get *EXACTLY* the same results doing
    Code:
    function addOne(element, index, array) 
    { 
        return element; 
    }
    And because *ONLY* the first element of the array has a value of zero, *THAT* element is the only one that is seen as "false" by JavaScript as the result of the call to the filter function.

    Try this, instead:
    Code:
    function addOne(element, index, array) { return (element++); }
    var Aarr = [0,1,0,3,4,5,0,7,0,9];
    var Barr = Aarr.filter(addOne);
    alert('Orig: \n'+Aarr+'\n\nadd 1: \n'+Barr);
    Your filter is saying "filter out all the elements that have a value of zero." Nohthing more.

    What were you *expecting* to happen??
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #3
    Regular Coder
    Join Date
    Aug 2010
    Posts
    974
    Thanks
    19
    Thanked 212 Times in 210 Posts
    function addOne(element, index, array) { return ++array[index]; }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    Aarr.filter(addOne);
    alert(Aarr);

  • #4
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Quote Originally Posted by DaveyErwin View Post
    function addOne(element, index, array) { return ++array[index]; }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    Aarr.filter(addOne);
    alert(Aarr);
    Well, yes. That will increment each element of the array by one.

    But you would get the same results using
    Code:
    function addOne(element, index, array) 
    { 
        ++array[index]; 
        return false; // or return Math.random() < 0.5; even 
    }
    The return value from filter has no impact on what happens in the *original* array.

    If you were to do this:
    Code:
    function addOne(element, index, array) { return ++array[index]; }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    var Barr = Aarr.filter(addOne);
    alert(Aarr + "\n" + Barr);
    You would find that Barr will contain a copy of the *original* array (0 through 9) while Aaar has every element incremented by one.

    But, again, if you started with (say)
    Code:
    var Aarr = [0,1,-1,3,-1,7,0];
    Then you would end up with
    Code:
    Aarr == [ 1,2,0,4,0,8,1 ]
    Barr == [ 0,1,3,7,0 ]
    Because the -1s on being increment become zero and so the return from filter is, effectively, false and those -1s get filtered out.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #5
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    So, again, I'm not at all sure what JMrker was trying to accomplish.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #6
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,133
    Thanks
    38
    Thanked 504 Times in 498 Posts

    Arrow

    Quote Originally Posted by Old Pedant View Post
    So, again, I'm not at all sure what JMrker was trying to accomplish.
    I was trying to add 1 to each element of the original array
    And see that the new array had values all incremented by one.

    I thought I could avoid a for...loop with a filter() command.

  • #7
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Well, it's kind of hacky, but you could do this:
    Code:
    function addOne(element, index, array) 
    { 
        ++array[index]; 
        return true; // just in case the element was -1 !!!
    }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    var temp = Aarr.filter(addOne);
    var Barr = Aarr;
    Aarr = temp;
    alert(Aarr + "\n" + Barr);
    No?

    But, really, it would be more sensible to maybe do this:
    Code:
    Array.prototype.addOne = function( )
    {
        var ar = [];
        for ( var i = 0; i < this.length; ++i )
        {
            ar[i] = this[i] + 1;
        }
        return ar;
    }
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    var Barr = Aarr.addOne();
    *SOMEWHERE* there has to be a loop through the elements. Granted, with filter() the loop is in native code, not JS, but then the call to the filter method for *each* element has to be tons slower than a simple loop in JS code. I would bet it would be an order of magnitude slower, in fact.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • Users who have thanked Old Pedant for this post:

    jmrker (01-05-2013)

  • #8
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,133
    Thanks
    38
    Thanked 504 Times in 498 Posts
    Thanks. I like the prototype version the best.

    Obviously I have a way to go to understand all the nuances of this JS language!

  • #9
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Another easy option:
    Code:
    <script type="text/javascript">
    Array.prototype.forEach = function( callback )
    {
        var ar = [];
        for ( var i = 0; i < this.length; ++i )
        {
            ar[i] = ( callback == null ) ? this[i] : callback( this[i] );
        }
        return ar;
    }
    
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    
    var Barr = Aarr.forEach( function(element) { return element + 17; } );
    
    var Carr = Barr.forEach( );
    
    alert(Aarr + "\n" + Barr + "\n" + Carr);
    </script>
    See it? Now you have a forEach method that in turn takes a function that allows you to mangle each element, one at a time, as you wish.

    As demonstrated with the var Carr = line, if you omit the callback function then forEach turns into a simple array copy. (Not efficient, but better than crashing when the callback is omitted.)
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #10
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    If you wanted, you could write that as
    Code:
    <script type="text/javascript">
    Array.prototype.forEach = function( callback )
    {
        var ar = [];
        for ( var i = 0; i < this.length; ++i )
        {
            ar[i] = ( callback == null ) ? this[i] : callback( this[i], this, i );
        }
        return ar;
    }
    
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    
    var Barr = Aarr.forEach( 
            function(element, array, index) 
            { 
                array[index] *= 100; 
                return element + 17; 
            } 
        );
    
    var Carr = Barr.forEach( );
    
    alert(Aarr + "\n" + Barr + "\n" + Carr);
    </script>
    So that the caller of forEach could (as demonstrated) optionally modify the original array at the same time he/she returns a value for the new array. Taking a lead from how filter( ) works.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #11
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,133
    Thanks
    38
    Thanked 504 Times in 498 Posts

    Question

    Well, you've managed to confuse the fool out of me.

    I started out trying to figure out the .filter() function,
    but have abandoned that because I thought I understood your Array.prototype function.

    Now I don't understand what's happening with your latest modification with the 'callback' addition.

    Here is the test code I am using to try to understand the working.
    In the comment section at the end, I list what I expected and what is displayed.
    ??? indicates that I don't understand the results.

    If you have the time, now that you have fogged my prior clarity, could you explain the
    difference between what I thought I know and what is really happening?
    (I may be testing your mind reading skills with that last statement).

    Code:
    <script type="text/javascript">
    Array.prototype.forEach = function( callback ) {
      var ar = [];
      for ( var i = 0; i < this.length; ++i ) {
        ar[i] = ( callback == null ) ? this[i] : callback( this[i], this, i );
      }
      return ar;
    }
    
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    
    var Barr  = Aarr.forEach( function(element) { return element + 1; } );
    var BBarr = Aarr.forEach( function(element, array, index) { array[index] += 1; return element; } );
    var Carr = Barr.forEach( );
    var Darr = Aarr.forEach( );
    var Earr = Aarr.forEach( function(element, array, index) { array[index] *= 100; return element; } );
    
    alert('A : '+Aarr + "\nB : "+Barr +'\nBB: '+BBarr + "\nC : "+Carr + "\nD : "+Darr + "\nE : "+Earr +"\nA : "+Aarr);
    
    /*
        Expected to see:
    A : 0,1,2,3,4,5,6,7,8,9
    B : 1,2,3,4,5,6,7,8,9,10
    BB: 1,2,3,4,5,6,7,8,9,10
    C : 1,2,3,4,5,6,7,8,9,10
    D : 0,1,2,3,4,5,6,7,8,9
    E : 0,100,200,300,400,500,600,700,800,900
    A : 0,1,2,3,4,5,6,7,8,9
     
        Actual display:
    A : 100,200,300,400,500,600,700,800,900,1000   ???
    B : 1,2,3,4,5,6,7,8,9,10
    BB: 0,1,2,3,4,5,6,7,8,9                        ???
    C : 1,2,3,4,5,6,7,8,9,10
    D : 1,2,3,4,5,6,7,8,9,10                       ???
    E : 1,2,3,4,5,6,7,8,9,10                       ???
    A : 100,200,300,400,500,600,700,800,900,1000   ???
    
    */
    </script>

  • #12
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Let's take them one at a time.

    First of all, understand this: Whatever the callback function *RETURNS* for each element it is called for is what ends up in the OUTPUT array. Anything the function does to the *ORIGINAL* array has *NO IMPACT AT ALL* on what happens to the output array.

    SO;
    Code:
    var Barr  = Aarr.forEach( function(element) { return element + 1; } )
    You are not changing any elements of the Aaar array; you are returning each element + 1. So each element of Barr will be one greater than Aarr.

    Great. That's what you expected. That's what you got.
    *********
    Code:
    var BBarr = Aarr.forEach( function(element, array, index) { array[index] += 1; return element; } );
    You are changing each element of the input array. But you are returning the *original* element value. So the output array (BBarr) will get a copy of the original array while, at the same time, each element of the original array is incremented by 1.

    Remember: the value of element *IS* the ORIGINAL value of the element. Changing the element value inside the current (thiis) array does *NOT* impact the value of element.

    I *THINK* that what you *MEANT* to do here was
    Code:
    var BBarr = Aarr.forEach( function(element, array, index) { array[index] += 1; return array[index]; } );
    Do you see the *HUGE* difference that makes? Here, you IGNORE the original value of element and go fetch it again as the return value. You could have also written this as
    Code:
    var BBarr = Aarr.forEach( function(element, array, index) { return ++array[index]; } );
    ************
    Code:
    var Earr = Aarr.forEach( function(element, array, index) { array[index] *= 100; return element; } );
    Same thing as with BBarr. You are modifying the input (this) array but returning the ORIGINAL element value, so Earr gets a copy of the input array before the modifications.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #13
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    If you do NOT want to modify the input (this) array, then do *NOT* use the array or index arguments that are passed to the callback! Simple as that.

    What you expected could have been produced via
    Code:
    var Aarr = [0,1,2,3,4,5,6,7,8,9];
    
    var Barr  = Aarr.forEach( function(element) { return element + 1; } );
    var BBarr = Aarr.forEach( function(element) { return ++element; /* same as element + 1 */ } );
    var Carr = Barr.forEach( );
    var Darr = Aarr.forEach( );
    var Earr = Aarr.forEach( function(element) { return element * 100; } );
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #14
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,206
    Thanks
    80
    Thanked 4,565 Times in 4,529 Posts
    Maybe if we look at the forEach method it will be clearer. It truly is simple.

    First of all, let me simplify it to *require* that the callback function be supplied:
    Code:
    Array.prototype.forEach = function( callback ) {
      // we build the new array here:
      var ar = [];
      // we loop through every element of the old array:
      for ( var i = 0; i < this.length; ++i ) {
        // and we put the RESULT of calling the callback function into
        // the corresponding element of the new array:
        ar[i] = callback( this[i], this, i );
      }
      // finally returning the new array:
      return ar;
    }
    So the only thing to really need to understand is this line:
    Code:
        ar[i] = callback( this[i], this, i );
    The assignment into the new array is hopefully obvious. So let's look at *HOW* the callback function is called:
    Code:
        callback( 
            this[i], /* the *VALUE* of the current (i-th) element of the array */
            this,  /* the *ENTIRE* current array */
            i /* the element number */
        );
    So you can see that in your callback function:
    Code:
    // using one of your example:
    function(element, array, index) 
    { 
        // remember: element is the *VALUE* of array[index]
        // but it was *ALREADY* fetched out of the array in the forEach method/function
        // so when we do this, we are indeed changing the element number index in the 
        // original array...
        array[index] += 1; 
        // but that in no way affects the value of element:
        return element; 
    }
    Did you perhaps forget that arrays are passed to functions *BY REFERENCE*? So the variable array in your callback function is 100% the same *OBJECT* as the array referred to by this in the forEach method.

    Clearer?
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • Users who have thanked Old Pedant for this post:

    jmrker (01-05-2013)

  • #15
    Senior Coder jmrker's Avatar
    Join Date
    Aug 2006
    Location
    FL
    Posts
    3,133
    Thanks
    38
    Thanked 504 Times in 498 Posts
    Thank you very much for that extensive discussion.
    It helps me and perhaps others who may view this thread.
    I appreciate your time.


  •  
    Page 1 of 2 12 LastLast

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •