0

I have a JSON query and I am using console.log to present it:

var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};

document.getElementById("original").innerHTML = json_data.rows;
 
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>

And for the value "one" I have two numbers (209 and 274).

I am then using a function to groupby which works fine (output). My problem is that when I use the console.log for the initial json_data query, the first two rows are different. It seems that my function replaced the first two rows with the rows of the output (red). The function is given here:

    function initialGroupBy(rows) {
  const 
    rowMap = new Map(),
    result = [],
    dataTemp = [];

  // Iterate over the rows.
  rows.forEach(row => {

    const
      // Create a key, it is the first elements joined together.
      key = row.slice(0,1).join();

    // Check if the Map has the generated key...
    if (rowMap.has(key)) {
      // The map has the key, we need to add up the values
      const
        // Get the value for the current key.
        storedRow = rowMap.get(key);
        // Add the value of the current row to the row in the map.
        storedRow[2] += row[2];

    } else {
      // The key doens't exist yet, add the row to the map.
      rowMap.set(key, row);
    }

  });

  // Iterate over all the entries in the map and push each value with the
  // summed up value into the array.
  rowMap.forEach(value => {
    result.push(value);
  });


    for (i = 0; i < result.length; i++) 
    {
    var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
    dataTemp.push(object2);      
    }

    return dataTemp;

}

A full snippet can be found here (Compare the first two rows of the yellow box from the two snippets):

var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};

function initialGroupBy(rows) {
  const 
    rowMap = new Map(),
    result = [],
    dataTemp = [];
    
  // Iterate over the rows.
  rows.forEach(row => {
  
    const
      // Create a key, it is the first elements joined together.
      key = row.slice(0,1).join();
      
    // Check if the Map has the generated key...
    if (rowMap.has(key)) {
      // The map has the key, we need to add up the values
      const
        // Get the value for the current key.
        storedRow = rowMap.get(key);
        // Add the value of the current row to the row in the map.
        storedRow[2] += row[2];
        
    } else {
      // The key doens't exist yet, add the row to the map.
      rowMap.set(key, row);
    }

  });
  
  // Iterate over all the entries in the map and push each value with the
  // summed up value into the array.
  rowMap.forEach(value => {
    result.push(value);
  });
    

 for (i = 0; i < result.length; i++) 
 {
    var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
    dataTemp.push(object2);      
 }

 return dataTemp;

}

const damn = initialGroupBy(json_data.rows);

 
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div style="background:yellow; "id="original"></div>
<br><br>
<div style="background:red;" id="output"></div>

I have tried changing the var to const in many cases. Am I missing a fundamental JavaScript case here?

Datacrawler
  • 2,780
  • 8
  • 46
  • 100

3 Answers3

0

A) The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable -> Link

B) Your problem is that you are actually editing the original object in the initialGroupBy function. Maybe this answer will be helpful.

0

As your value is an Array object, when you save it in your temporary Map, you're actually using a reference to your original data structure row.

So in the first forEach loop, where you sum the values you're actually changing the original array entry.

The solution is pretty simple, just clone the array:

rowMap.set(key, row.slice());

Another possibility is to use a different array to save the totals.

Here is your code with the fix.

var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};

function initialGroupBy(rows) {
  const 
    rowMap = new Map(),
    result = [],
    dataTemp = [];
    
  // Iterate over the rows.
  rows.forEach(row => {
  
    const
      // Create a key, it is the first elements joined together.
      key = row.slice(0,1).join();
      
    // Check if the Map has the generated key...
    if (rowMap.has(key)) {
      // The map has the key, we need to add up the values
      const
        // Get the value for the current key.
        storedRow = rowMap.get(key);
        // Add the value of the current row to the row in the map.
        storedRow[2] += row[2];
        
    } else {
      // The key doens't exist yet, add the row to the map.
      rowMap.set(key, row.slice());
    }

  });
  
  // Iterate over all the entries in the map and push each value with the
  // summed up value into the array.
  rowMap.forEach(value => {
    result.push(value);
  });
    

 for (i = 0; i < result.length; i++) 
 {
    var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
    dataTemp.push(object2);      
 }

 return dataTemp;

}

const damn = initialGroupBy(json_data.rows);

 
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
Datacrawler
  • 2,780
  • 8
  • 46
  • 100
Mario Santini
  • 2,905
  • 2
  • 20
  • 27
0

A different logic applies here and the result is handy:

var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};


function groupBy(accumulator, item) {
const [date,extra,value] = item;

const key = date.slice(0,7);

if(!accumulator[key]){
accumulator[key] = 0
}

accumulator[key] += value;
return accumulator;

}

var damn = json_data.rows.reduce(groupBy,{});

damn = Object.keys(damn).map(function(key){
return {date: key, Value: "Total", num: damn[key]};
})

document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
Datacrawler
  • 2,780
  • 8
  • 46
  • 100