0

I have this piece of code, but whenever I run findTimeSlots() it immediately messes up my global array apptData but I don't see in anywhere in this function that is supposed to have changed it.

(ConsoleLog)

(index):69 
(4) [{…}, {…}, {…}, {…}]
0: {service: "A, B", duration: 50, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A, D", duration: 60, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)
(index):45 
(4) [{…}, {…}, {…}, {…}]
0: {service: "A, B", duration: 50, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A, D", duration: 60, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var apptData = [];
function addApptData(serviceName, rawDuration, selectedTech){
    apptData.push({
        'service': serviceName,
        'duration': rawDuration,
        'tech' : selectedTech
    })
}
function reduceApptData(index){
    apptData.splice(index, 1);
}
function findTimeSlots(dateStr){
    console.log(apptData);             //* Index 45 *//
    var final = new Array();
    var service, duration;
    for(var i = 0; i < apptData.length; i++){
        var duplicated = false;
        for(var j = 0; j < final.length; j++){
            if(final[j].tech == apptData[i].tech){
                service = ", "+apptData[i].service;
                final[j].service += service;
                duration = apptData[i].duration;
                final[j].duration += duration;
                duplicated = true;
                break;
            }
        }
        if(!duplicated){
            final.push(apptData[i]);
        }
    }
 }
addApptData("A", 20, "C");
addApptData("B", 30, "C");
addApptData("A", 20, "A");
addApptData("D", 40, "A");
console.log(apptData);                //* Index 69 *//
// If I remove the line below, I get the expected result, when I try to 
// run with this line it will mess up apptData[]
findTimeSlots("");

What I expected to be is

0: {service: "A", duration: 20, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A", duration: 20, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)

So basically I expect it to remain the same.

I wanted to consolidate my var apptData into my var final inside my findTimeSlots()

After debugging, I found out that my apptData keep changing unexpectedly for some reason.

I suspect this to be very trivial but I cannot for the life of me figure out.

  • That's why I love FP and it's immutability. –  Apr 05 '19 at 02:17
  • 2
    `final.push(apptData[i])` pushes an object without copying it, `final[j].service += service;` and `final[j].duration += duration;` modify objects that were pushed. – Ry- Apr 05 '19 at 02:22
  • 2
    When you assign objects in JavaScript you're not making a copy, you're just assigning a reference to the same object. So when `findTimeSlots()` modifies `final[j].property`, it's also modifying `apptData`. – Barmar Apr 05 '19 at 02:22
  • See https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object for ways to clone a JavaScript object. – Barmar Apr 05 '19 at 02:23

3 Answers3

1

You should make a shallow copy of apptData.

One thing you should take note of JavaScript is the difference between assignment by value vs assingment by reference.

When primitive data types such as numbers(i.e. 2, 23), strings (i.e 'aaa', '123') are assigned to a variable, the variable contains the primitive value.

On the other hand, when non-primitive data types (arrays, objects, functions) are assigned to a variable, the variable contains the reference (which 'points' to the object's location on the memory), rather than the value itself.

To answer your question, you should be making a shallow copy of apptData, rather than making a direct reference to it.

Assuming that your apptData is an array of (non-nested) objects, here is a simply way to make a shallow copy of it using ES6's spread syntax:

const apptDataCopy = apptData.map(serviceObject => ({...serviceObject}));

From there, you can make any changes you wish to apptDataCopy, and it will not affect the original apptData.

wentjun
  • 40,384
  • 10
  • 95
  • 107
1

What is happening is explained in this tiny example:

let a = {test: 10};
let b = a;
b.text = 11;
console.log(a); //this will print {test: 11}

Since a is an object, when you assign let b = a; you are actually saving a reference to a, not cloning a.

You need to clone the object apptData[i], and to do that See this answer

So instead of final.push(apptData[i]); you should push a cloned apptData[i].

Hope that helps.

Ali Elkhateeb
  • 3,413
  • 4
  • 21
  • 39
1
var apptData = [], typeSelectedTech = new Set(), finalData =[], tmpObj = {serviceName: '', rawDuration: 0}
const addApptData = (serviceName, rawDuration, selectedTech)=> {
    apptData.push({
        serviceName,
        rawDuration,
        selectedTech
    })
    if (typeSelectedTech.has(selectedTech)) {
      finalData.push({serviceName: tmpObj.selectedTech + ',' + serviceName, rawDuration: tmpObj.rawDuration + rawDuration, selectedTech})
    }else {
      tmpObj = {
          serviceName,
          rawDuration,
          selectedTech
      }
      typeSelectedTech.add(selectedTech)

    }
}

addApptData("A", 20, "C");
addApptData("B", 30, "C");
addApptData("A", 20, "A");
addApptData("D", 40, "A");
console.log(finalData);
console.log(apptData);

First of all, I believe that you have solved the problem, my answer is only for communication.

I tried the rewriting method, you can get the raw data and the target data. Although it produces multiple global variables, I can't think of other ways.

So as a communication, I hope other people can have a better way.

LFR502
  • 56
  • 4