Go Back   CodingForums.com > :: Client side development > JavaScript programming

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 01-04-2013, 09:31 PM   PM User | #1
jmrker
Senior Coder

 
jmrker's Avatar
 
Join Date: Aug 2006
Location: FL
Posts: 2,763
Thanks: 29
Thanked 453 Times in 447 Posts
jmrker will become famous soon enough
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.
jmrker is offline   Reply With Quote
Old 01-04-2013, 10:05 PM   PM User | #2
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-04-2013, 10:06 PM   PM User | #3
DaveyErwin
Regular Coder

 
Join Date: Aug 2010
Posts: 806
Thanks: 12
Thanked 168 Times in 166 Posts
DaveyErwin is on a distinguished road
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);
DaveyErwin is offline   Reply With Quote
Old 01-04-2013, 10:16 PM   PM User | #4
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-04-2013, 10:18 PM   PM User | #5
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-04-2013, 10:39 PM   PM User | #6
jmrker
Senior Coder

 
jmrker's Avatar
 
Join Date: Aug 2006
Location: FL
Posts: 2,763
Thanks: 29
Thanked 453 Times in 447 Posts
jmrker will become famous soon enough
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.
jmrker is offline   Reply With Quote
Old 01-04-2013, 11:02 PM   PM User | #7
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Users who have thanked Old Pedant for this post:
jmrker (01-04-2013)
Old 01-04-2013, 11:30 PM   PM User | #8
jmrker
Senior Coder

 
jmrker's Avatar
 
Join Date: Aug 2006
Location: FL
Posts: 2,763
Thanks: 29
Thanked 453 Times in 447 Posts
jmrker will become famous soon enough
Thanks. I like the prototype version the best.

Obviously I have a way to go to understand all the nuances of this JS language!
jmrker is offline   Reply With Quote
Old 01-04-2013, 11:52 PM   PM User | #9
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-04-2013, 11:59 PM   PM User | #10
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-05-2013, 01:45 AM   PM User | #11
jmrker
Senior Coder

 
jmrker's Avatar
 
Join Date: Aug 2006
Location: FL
Posts: 2,763
Thanks: 29
Thanked 453 Times in 447 Posts
jmrker will become famous soon enough
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>
jmrker is offline   Reply With Quote
Old 01-05-2013, 02:06 AM   PM User | #12
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-05-2013, 02:08 AM   PM User | #13
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Old 01-05-2013, 02:23 AM   PM User | #14
Old Pedant
Supreme Master coder!

 
Old Pedant's Avatar
 
Join Date: Feb 2009
Posts: 23,168
Thanks: 59
Thanked 3,993 Times in 3,962 Posts
Old Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to allOld Pedant is a name known to all
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.
Old Pedant is offline   Reply With Quote
Users who have thanked Old Pedant for this post:
jmrker (01-05-2013)
Old 01-05-2013, 02:32 AM   PM User | #15
jmrker
Senior Coder

 
jmrker's Avatar
 
Join Date: Aug 2006
Location: FL
Posts: 2,763
Thanks: 29
Thanked 453 Times in 447 Posts
jmrker will become famous soon enough
Thank you very much for that extensive discussion.
It helps me and perhaps others who may view this thread.
I appreciate your time.
jmrker is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 09:06 PM.


Advertisement
Log in to turn off these ads.