2

I am encountering a weird problem with what I believe to be the way Javascript references variables. The project I am working in is Typescript, but this functionality is from vanilla Javascript.

I have an object all_obj set further up in the code.

The following code:

all = [];
data = [];

console.log(all_obj);

Outputs:

{
  "1593561600000": {
    "date": 1593561600000,
    "volume": 24463,
    "value": 165049285,
    "rank": 0
  },
  "1596240000000": {
    "date": 1596240000000,
    "volume": 24366,
    "value": 158841976,
    "rank": 0
  },
  "1604188800000": {
    "date": 1604188800000,
    "volume": 30034,
    "value": 196655815,
    "rank": 0
  },
  This goes on 9 more times with similar objects
}

This, works as expected.

However the following code somehow logs something completely different:

let all = [];
let data = [];

console.log(all_obj);

for (let key of Object.keys(all_obj)) {
  all.push(all_obj[key]);
}

this.data.push(
  {
    brand: 'all_aggregated',
    datapoints: [...all],
  },
  {
    brand: 'all_mean',
    datapoints: [...all].map((dp: brand_datapoint) => {
      dp.value = +(dp.value / dp.volume).toFixed(2);

      return dp;
    }),
  }
);

It logs this:

{
  "1593561600000": {
    "date": 1593561600000,
    "volume": 24463,
    "value": 6746.89,
    "rank": 0
  },
  "1596240000000": {
    "date": 1596240000000,
    "volume": 24366,
    "value": 6519,
    "rank": 0
  },
  "1604188800000": {
    "date": 1604188800000,
    "volume": 30034,
    "value": 6547.77,
    "rank": 0
  },
  9 more like this
}

As you can see, in the second example. The "value"'s within the all_obj's children is now in the thousands, instead of the hundreds of millions. Also, it is formatted to 2 decimal places. This is suspiciously the formatting I am doing in this line towards the end of the second code example:

dp.value = +(dp.value / dp.volume).toFixed(2);

How on earth is changing the values propagating back up the code???

Especially through [...all]

This is running in an Angular Project. I have trimmed off the bloat, if you need more info of course ask for it, but I didn't want to paste 300 lines of code in here :)

I have been fighting with this for about 6 hours now, and have completely rewritten the same code in about 15 different ways. So I am completely baffled what is causing this.

Solution! The issue isn't that the data is propagating back up to all_obj, it's that values were being set in the pink arrow, and were being picked up in the red arrow because they were just referencing to it. I thought [...all] would fix it but that just builds a new array with the same references.

enter image description here

1 Answers1

1

The problem is that you are editing the value on the same object that you started with.

When you do all.push(all_obj[key]);, you are filling all with a bunch of references to the inner objects in all_obj. Since these are simply references, interacting with them changes the same objects as the the ones in all_obj. So when you set the value dp.value =, it is "propagating back up the code" because it is the same object that is inside all_obj.

Since you want to make these changes on a new object for just formatting reasons, you should make a copy of the object and only edit the values there. Since the datapoints seem to be simple, with only simple numbers as properties, you can easily make a copy with

dp = {...dp};

You should do this before you change the value.

You may be thinking that this shouldn't be needed, because of the [...all]. However, that will only make a copy of the all list itself, not of all the content. So basically it is a "new list" that contains all the same references to all the same objects.

You may be interested in Is JavaScript a pass-by-reference or pass-by-value language?

EDIT:

Taking another look, it would probably be better/cleaner to make the copy right as you are building the all array. So instead of doing

all.push(all_obj[key]);

You can do

all.push({...all_obj[key]});

Then you don't need to worry about changing the original data as you manipulate the points for formatting or display.

Luke B
  • 2,075
  • 2
  • 18
  • 26
  • 1
    You are a star. I was doing all_obj = {...all_obj}, then I thought maybe that doesn't redeclare it but just put references to it's children in a new object. So I was doing JSON.parse(JSON.stringify()) etc. And I was wondering why it wasn't working. But the whole time it was the issue of remaking dp. Thank you! – Samuel Pollard Aug 27 '21 at 00:36