5

I pass 2 arrays to a function and want to move a specific entry from one array to another. The moveDatum function itself uses underscorejs' methods reject and filter. My Problem is, the original arrays are not changed, as if I was passing the arrays as value and not as reference. The specific entry is correctly moved, but as I said, the effect is only local. What do I have to change, to have the original arrays change as well?

Call the function:

this.moveDatum(sourceArr, targetArr, id)

Function itself:

function moveDatum(srcDS, trgDS, id) {
    var ds = _(srcDS).filter(function(el) {
        return el.uid === uid;
    });
    srcDS = _(srcDS).reject(function(el) {
        return el.uid === uid;
    });
    trgDS.push(ds[0]);
    return this;
}

Thanks for the help

David M
  • 99
  • 6
  • 1
    You can't do this with `.filter()` or `.reject()` because they create new arrays. You can change the content of `targetArr` by modifying `trgDS` with `trgDS.push(ds[0])` (like you are doing), because both refer to the same array, but you can't cause `sourceArr` to refer to a new array by assigning `srcDS` to a new array. – nnnnnn Aug 24 '13 at 13:07
  • Maybe you can find this question and explanations useful: http://stackoverflow.com/questions/6605640/javascript-by-reference-vs-by-value – Stefan Aug 24 '13 at 13:08
  • @nnnnnn Thanks, I think I understand now what is going on, basically I reference srcDS to the new array created with reject() and lose the reference to the originally passed array that I actually want to change, would there be an easy way to fix this? – David M Aug 24 '13 at 13:14

2 Answers2

3

As mentioned in the comments, you're assigning srcDS to reference a new array returned by .reject(), and thus losing the reference to the array originally passed in from outside the function.

You need to perform your array operations directly on the original array, perhaps something like this:

function moveDatum(srcDS, trgDS, id) {
    var ds;
    for (var i = srcDS.length - 1; i >= 0; i--) {
        if (srcDS[i].uid === id) {
           ds = srcDS[i];
           srcDS.splice(i,1);
        }
    }
    trgDS.push(ds);
    return this;
}

I've set up the loop to go backwards so that you don't have to worry about the loop index i getting out of sync when .splice() removes items from the array. The backwards loop also means ds ends up referencing the first element in srcDS that matches, which is what I assume you intend since your original code had trgDS.push(ds[0]).

If you happen to know that the array will only ever contain exactly one match then of course it doesn't matter if you go forwards or backwards, and you can add a break inside the if since there's no point continuing the loop once you have a match.

(Also I think you had a typo, you were testing === uid instead of === id.)

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
2

Copy over every match before deleting it using methods which modify Arrays, e.g. splice.

function moveDatum(srcDS, trgDS, id) { // you pass an `id`, not `uid`?
    var i;
    for (i = 0; i < srcDS.length; ++i) {
        if (srcDS[i].uid === uid) {
            trgDS.push(srcDS[i]);
            srcDS.splice(i, 1); 
            // optionally break here for just the first
            i--; // remember; decrement `i` because we need to re-check the same
                 // index now that the length has changed
        }
    }
    return this;
}
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • @macg if you think Paul's answer was helpful consider to upvote it please. – Stefan Aug 24 '13 at 15:05
  • @Stefan I did consider it, but stackoverflow is not very considerate with me having only 13 reputation =) I need two more to upvote – David M Aug 27 '13 at 20:12
  • @macg I thought you could vote on answers to questions you asked yourself. Anyway; there. – Paul S. Aug 27 '13 at 20:28