简体   繁体   中英

Weird duplication when saving object properties to array

JSFiddle

I want to loop through an object, perform some changes to each of it's properties, and then push those to an array. Each object property is pushed multiple times (in the JSFiddle, I've set it to push twice for simplicity). Each 'iteration' has a few of the properties different (JSFiddle shows only 1, ie the 'number').

However, it seems that all objects being pushed in a single loop cannot possess unique properties. I'm looking for a solution for this.

Sorry for my poor English, it's difficult to explain issue and it would be easier to see the JSFiddle.

Actual output:

[{ x: 1, number: 1 },  
{ x: 1, number: 1 },  
{ y: 2, number: 1 },  
{ y: 2, number: 1 },  
{ z: 3, number: 1 },  
{ z: 3, number: 1 }] 

Expected output:

[{ x: 1, number: 0 },
{ x: 1, number: 1 },
{ y: 2, number: 0 },
{ y: 2, number: 1 },
{ z: 3, number: 0 },
{ z: 3, number: 1 }]

Code:

var items = { 
  "a": {
    "x": 1
  },
  "b": {
    "y": 2
  },
  "c": {
    "z" : 3
  }
};

var something = [];

for ( var key in items ) {
  var item = items[key];
  if ( items.hasOwnProperty(key) ) {
    for ( var i = 0; i < 2; i++ ) {
      item.number = i;
      something.push(item);
    }
  }
}

console.log(something);

I'll explain by dry-running. In the below loop, we pick the first key which is "a" in your 'items' object. Now lets get inside the loop

    for ( var key in items ) {

      //key has been set to "a"

     var item = items[key]; 

     //item now "refers" to the object items["a"] which is {"x":1}

      if ( items.hasOwnProperty(key) ) {
        for ( var i = 0; i < 2; i++ ) {

          //in loop 1 item is referring to {"x":1}
          //in loop 2 item is referring to {"x":1,"number":0} (modified in first iteration)

          item.number = i;

         //in loop 1 item gets "number" property and object becomes {"x":1,"number":0} in memory
         //in loop 2 it will still modify the same object. Hence {"x":1,"number":0} becomes {"x":1,"number":1}


          something.push(item);
          //in loop1 reference to the object gets pushed.
          //in loop2 reference to the object gets pushed. 
          //both references (items) essentially point to same object.

        }
      }
    }

You would need to create a copy of your object rather than multiple references to get the expected output. In Javascript, cloning of object is not straight forward. You can refer this link How do I correctly clone a JavaScript object?

However a simple solution to your current code will be to create a clone function and use it without worrying about deep-cloning issues and prototype-inherited properties. Here's the modified code.

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}
var items = {
  "a": {
    "x": 1
  },
  "b": {
    "y": 2
  },
  "c": {
    "z" : 3
  }
};



var something = [];

for ( var key in items ) {
  var item = items[key];

  if ( items.hasOwnProperty(key) ) {
    for ( var i = 0; i < 2; i++ ) {
      var tempItem = clone(item);
      tempItem.number = i;
      something.push(tempItem);
    }
  }
}

console.log(something);

Upvote if you find it useful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM