Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array.push() makes all elements the same when pushing an object

I'm new to node and javascript and have been banging my head on the following. I've created an object as follows:

var Subscriber = {
'userID': String,
'email': String,
'name': String,
'stage': String,
'poster': Boolean,
'canEmail': Boolean,
'stage': String, }

I have a function where I query mongodb, and loop through the results, attempting to load an array of subscribers, which I've declared as:

var s = Subscriber;
var subscribers = [];

The loop looks like this:

//load array of users that are subscribed to the group
        async.forEach(g.subscribers, function(item, callback) {     
            //load user document for this user
            User.findOne({ _id: item}, function(err, u) {
                if(!err && u) {                 
                    //var s = new Subscriber();
                    console.log('Sub load, found user %s, building array item', u.email);
                    console.log('Subs @ loop start');
                    console.log(util.inspect(subscribers));

                    console.log('Heres foo: ' + util.inspect(foo));


                    s.userID = u._id;
                    s.email = u.email;
                    s.name = u.firstName + ' ' + u.lastName;
                    s.stage = u.stage;
                    s.poster = false; //we're just loading subscribers at this point'
                    if(s.stage != 'new') s.canEmail = true;

                    //push new subscriber onto the array
                    console.log('Pushing ' + util.inspect(s));
                    subscribers.push(s);

                    console.log('At end ' + util.inspect(subscribers));

                    foo.push(s.email);
                    console.log('Heres foo now: ' + util.inspect(foo));

                    callback(null, item);
                }

After each call to subscribers.push(s), the array has the correct number of elements, but all elements match the last values for s, like this (with two different users being pulled from the DB):

[ { userID: 4fc53a71163006ed0f000002,
email: '[email protected]',
name: 'undefined undefined',
stage: 'new',
poster: false,
canEmail: true },
  { userID: 4fc53a71163006ed0f000002,
email: '[email protected]',
name: 'undefined undefined',
stage: 'new',
poster: false,
canEmail: true } ]

Pushing a single element of s rather than the whole object seems to be fine. I added the "foo" array as a test, and it works fine:

Heres foo now: [ '[email protected]', '[email protected]' ]

What is going on here?!?!??!

like image 728
pat Avatar asked Jun 07 '12 13:06

pat


People also ask

What does array push () do?

The push() method adds one or more elements to the end of an array and returns the new length of the array.

Does push change array?

The push() method adds new items to the end of an array. The push() method changes the length of the array. The push() method returns the new length.

Is array push constant?

The Array. push() has a Constant Time Complexity and so is O(1). All it does is add an element and give it an index that's 1 greater than the index of the last element in the array.

Does array push change the original array?

Examples of push in JavaScript and common errors Reassigning the array with the output from push is a common error. To avoid this error you need to remember that push changes the array, and returns the new length. If you reassign the variable with the return value from push() you are overwriting the array value.


2 Answers

The problem is not with the push method of the Array.prototype but with your bindings. You are modifying the same s object in every iteration in your async.foreach block which is actually the same Object as the previously defined Subscriber.

First you should move the declaration of the s variable to the foreach block.

And also if you want to create an object with default values, it should be a function, which returns a new object:

function Subscriber() {
  return {
    'userID':   '',
    'email':    '',
    'name':     '',
    'stage':    '',
    'poster':   false,
    'canEmail': false,
    'stage':    ''
  };
};

And then you can instantiate a Subscriber object like this:

var s = Subscriber();

See this answer or Closures on MDN for more explanation.

like image 56
KARASZI István Avatar answered Sep 20 '22 19:09

KARASZI István


Cloning the object before pushing into the array, also solves the problem.

temp = clone(s);
subscribers.push(temp);

Get https://www.npmjs.com/package/clone

like image 31
Ben Avatar answered Sep 19 '22 19:09

Ben