0

Trying to build a simple JSON object that contains an array of objects.

var forms = {};
var form = {};

forms.ID = 1;
forms.PackageName = "MyPackage";
forms.PackageForms = [];

form.Key = "0001XX";
form.Ordinal = 0;
forms.PackageForms.push(form);

form.Key = "0002XX";
form.Ordinal = 1;
forms.PackageForms.push(form);

but this Results in:

{"ID":1,"PackageName":"MyPackage","PackageForms":[{"Key":"0002XX","Ordinal":1},{"Key":"0002XX","Ordinal":1}]}

I understand why this is happening: var 'form' is still referring to the just pushed object. Without creating a different object for each item how can I reuse the var 'form'? This must be simple but it is eluding me.

UPDATE:

This seems to work and looks pretty clean

    var forms = {};
    forms.ID = 1;
    forms.PackageName = "MyPackage";
    forms.PackageForms = [];

    function Form(Key, Ordinal) {
        this.Key = Key;
        this.Ordinal = Ordinal;
    }

    forms.PackageForms.push(new Form("0001XX", 0));
    forms.PackageForms.push(new Form("0002XX", 1));
kpg
  • 589
  • 6
  • 28
  • 1
    why not use a new object? it's a simple statement without any side effects. all other solution makes it harder to understand. – Nina Scholz Dec 06 '18 at 16:52
  • 1
    Why do you build this structure in such a cumbersome way, property by property, instead of just using an Object literal? – Thomas Dec 06 '18 at 16:54
  • @Thomas - I'm testing a webservice with some data, eventually this will be cleaned up, my down and dirty test gave me an odd result though. – kpg Dec 06 '18 at 16:57
  • Seems there are a lot of ways to do it, I found the one way not to do it. – kpg Dec 06 '18 at 17:02

3 Answers3

1

You can either create a new object each time:

forms.PackageForms.push(form);
form = {};

or you can push a copy of the object:

forms.PackageForms.push(Object.assign({}, form));
dave
  • 62,300
  • 5
  • 72
  • 93
  • Object.assign() does work. I don't think I will use it but glad to discover it, that could come in handy. – kpg Dec 06 '18 at 17:09
0

The result you observed is the combination of two behaviours:

  • array.push() adds a new reference to the input object. This means that the same object shall be contained into the same array several times.
  • Assigning values to an object's property means you are changing that object.

So, there you are: You are adding the same object to the array several times, and changing (overwriting) its properties twice. That is why you find the same object twice into the array, and it has the last values you assigned to it.

Update

If you want to store different objects into the array, you must instantiate each time a new one, for example with new Object(), and then assign values to its properties.

Little Santi
  • 8,563
  • 2
  • 18
  • 46
0

A more typical way of achieving this in JavaScript is to not declare empty objects ({}) at all, but to use object literal syntax:

let forms = {
  ID: 1,
  PackageName: 'MyPackage',
  PackageForms: [
    {
      Key: '0001',
      Ordinal: 1
    },
    {
      Key: '0002',
      Ordinal: 2
    }
  ]
};

console.log(forms);

Update: from OP’s comment, here’s a rough outline of how I’d go about using map:

let rawFormData = ...;  // From wherever; an array of objects

let packageForms = rawFormData.map(form => {
  // Any processing, local variables, etc required to derive `Key` and `Ordinal` from `form`

  return {
    Key: form.getKey(),          // Or whatever -
    Ordinal: form.getOrdinal()   //   these could be any expression
  };
});

let forms = {
  ID: 1,
  PackageName: 'MyPackage',
  PackageForms: packageForms
};
MTCoster
  • 5,868
  • 3
  • 28
  • 49
  • the eventual object array will be built by looping over a table with a jquery for each. – kpg Dec 06 '18 at 17:21
  • Instead of using `forEach`, you could use `map` (see [here](https://stackoverflow.com/a/34426481/1813169) for a full explanation of the difference) which returns an array directly. Then just use that array in place of the array literal `[ ... ]` when you create `forms`. – MTCoster Dec 06 '18 at 17:28