How to parse arrays back and forth to function in a smart way

Posted on

Problem

Inside my main function I got a lot of these.

var statsArray = [];
statsArray = pushStats(r, htmlRecp, '', statsArray);
statsArray = pushStats(e, '', htmlExpe, statsArray);
etc...

Then I got statsArray function where I insert or update new items in the array.

function pushStats(currKey, lineRecp, lineExpe, statsArray){
    var selectStats = statsArray.filter(function(obj){
        return obj.key == currKey
    })
    if (!selectStats.length) {
        statsArray.push(
            {
                'key'       : currKey,
                'receipt'   : lineRecp,
                'expense'   : lineExpe
            }
        )
    } else {
        if (lineRecp) {
            selectStats[0].receipt = lineRecp;
        } else {
            selectStats[0].expense = lineExpe;
        }
    }
    return statsArray;
}       

My question is this, how can I do this smarter?

statsArray = pushStats(aa, bb, cc, statsArray);

Is this the only way to do this? To send statsArray back and forth like this?

Solution

My question is this, how can I do this smarter?

statsArray = pushStats(aa, bb, cc, statsArray);

Is this the only way to do this? To send statsArray back and forth like this?

This question is the result of a misunderstanding: You’re not “passing [the array] back and forth.” Arrays are objects. Variables don’t directly contain objects, they contain a value called an object reference which says where the object is elsewhere in memory. So what you’re doing is passing that object reference into your function and returning that reference out of it. The actual array isn’t passed into and out of the function.

Since you’re not changing the value of statsArray in your function (you’re just changing state the object it refers to contains), you don’t need to return it, and you don’t need to assign that return value. Just remove the return statsArray; from the end of the function and

var statsArray = [];
pushStats(r, htmlRecp, '', statsArray);
pushStats(e, '', htmlExpe, statsArray);
// ...

Let’s explain why that works:

After this line

var statsArray = [];

in memory you have this:

                    +−−−−−−−−−−−+
statsArray−−−−−−−−−>|  (Array)  |
                    +−−−−−−−−−−−+
                    | length: 0 |
                    +−−−−−−−−−−−+

The variable statsArray contains an object reference for the array, which exists elsewhere in memory. (An object reference is a bit like a memory address.)

That means that during the first call:

pushStats(r, htmlRecp, '', statsArray);

…the statsArray parameter contains that same reference, and both the variable and the parameter refer to the same array:

                            +−−−−−−−−−−−+
statsArray−−−−−−−−−−−+−−−−−>|  (Array)  |
                    /       +−−−−−−−−−−−+
                    |       | length: 0 |
                    |       +−−−−−−−−−−−+
+−−−−−−−−−−−−−−−−+  |
| (Call Context) |  |
+−−−−−−−−−−−−−−−−+  |
| statsArray     |−−+
| ...            |
+−−−−−−−−−−−−−−−−+

Changes that pushStats makes to the state of the array change the array, not the reference. So if pushStats pushes a new entry onto the array, you get this:

                            +−−−−−−−−−−−+
statsArray−−−−−−−−−−−+−−−−−>|  (Array)  |
                    /       +−−−−−−−−−−−+
                    |       | length: 0 |      +−−−−−−−−−−+
                    |       | 0         |−−−−−>| (Object) |
+−−−−−−−−−−−−−−−−+  |       +−−−−−−−−−−−+      +−−−−−−−−−−+
| (Call Context) |  |                          | ...      |
+−−−−−−−−−−−−−−−−+  |                          +−−−−−−−−−−+
| statsArray     |−−+
| ...            |
+−−−−−−−−−−−−−−−−+

After the call returns, the call context is cleaned up, and you have

                    +−−−−−−−−−−−+
statsArray−−−−−−−−−>|  (Array)  |
                    +−−−−−−−−−−−+
                    | length: 0 |      +−−−−−−−−−−+
                    | 0         |−−−−−>| (Object) |
                    +−−−−−−−−−−−+      +−−−−−−−−−−+
                                       | ...      |
                                       +−−−−−−−−−−+

The key thing here is that the array is an object, and pushStats just changes the state of that object, via the reference you pass into it.


Side note: You can make the function a bit more efficient using Array#find instead of Array#filter:

function pushStats(currKey, lineRecp, lineExpe, statsArray){
    var selectStats = statsArray.find(function(obj){
        return obj.key == currKey
    });
    if (!selectStats) {
        statsArray.push(
            {
                'key'       : currKey,
                'receipt'   : lineRecp,
                'expense'   : lineExpe
            }
        );
    } else {
        if (lineRecp) {
            selectStats.receipt = lineRecp;
        } else {
            selectStats.expense = lineExpe;
        }
    }
    return statsArray;
} 

It needs a polyfill for obsolete browsers, but it has the advantage that it doesn’t create an unnecessary array, and stops looping through statsArray as soon as it finds the entry you want.

There is really no reason to return the passed array from the function.

You can simply code it like this;

var statsArray = [];
pushStats(r, htmlRecp, '', statsArray);
pushStats(e, '', htmlExpe, statsArray);
etc...

function pushStats(currKey, lineRecp, lineExpe, statsArray){
    var selectStats = statsArray.filter(function(obj){
        return obj.key == currKey
    })
    if (!selectStats.length) {
        statsArray.push(
            {
                'key'       : currKey,
                'receipt'   : lineRecp,
                'expense'   : lineExpe
            }
        )
    } else {
        if (lineRecp) {
            selectStats[0].receipt = lineRecp;
        } else {
            selectStats[0].expense = lineExpe;
        }
    }
   // --> No more return
}

Leave a Reply

Your email address will not be published. Required fields are marked *