Julian Martinez
Julian Martinez

Reputation: 113

Inner function does not return changes to variable assigned in outer function

I am attempting to modify the masterCounter variable within the timeKeyAdditionCheck function. Within the timeKeyAdditionCheck function, I successfully assign a value to masterCounter, but this change is not reflected within the scope of getEventsWithTime. When timeKeyAdditionCheck is complete, the value of masterCounter returns to null.

What changes do I need to make with timeKeyAdditionCheck function?

let masterCounter = null;
let userTracker = {}; 

let timeKeyAdditionCheck = ('hour') => {
    assert((range == 'minute' || range == 'hour'), "In calcArrayof... range value needs to equal 'minute' or 'hours'")
    
    if (masterCounter == null) {
        masterCounter = [{timerange: event.timestamp, totalusercount: 0, totalvalidatorcount: 0, totaletherdeposited: 0}]
    }
    if (event.timestamp > (masterCounter[masterCounter.length - 1] + 3599)) {
        
            let differenceInTime = event.timestamp - (masterCounter[masterCounter.length - 1] + 3599);
            let timeKeysNeeded = Math.ceil(differenceInTime / 3600);
        
            i = 0;
            while (i < timeKeysNeeded) {
                
                let newEntry = masterCounter[masterCounter.length - 1];
                newEntry.timerange = newEntry.timerange + 3600;
                masterCounter.push(newEntry);
                i++;
            }
        }
}
(async () => {
    let events = await getEventsWithTime(3085928,3089928);
    
    for (event of events) {
        timeKeyAdditionCheck('hour');
        checkNewUsers();
        addValidatorsAndEth();    
    }

    convertToCsv(masterCounter)
    console.log(masterCounter)
  })()

Upvotes: 0

Views: 56

Answers (3)

Julian Martinez
Julian Martinez

Reputation: 113

The reason I wasn't getting the expected output for masterCounter was because, in the timeKeyAdditionCheckfunction, I thought I was making a copy of the object in the masterCounter array, but I actually created a reference instead. Here is the moment in my code when I unintentionally created a reference instead of a copy:

let newEntry = masterCounter[masterCounter.length - 1];

When I thought I was adding a unique object to the array, I was instead adding the reference to that same object at the end of the array.

I fixed it using the following code:

while (i < timeKeysNeeded) {
                
   let lastObjectRef = masterCounter[masterCounter.length - 1];
   let newEntry = Object.assign({}, lastObjectRef)
   newEntry.timerange = newEntry.timerange + 60;
   masterCounter.push(newEntry);
   i++;
}

I used Object.assign() to create a copy of the last object in the array instead of creating another reference.

Upvotes: 0

Scott Marcus
Scott Marcus

Reputation: 65796

Because masterCounter is not declared within timeKeyAdditionCheck (it's assigned there, but not declared there), masterCounter is implicitly declared as a Global variable. But, in getEventsWithTime, you do declare masterCounter so that declaration "hides" the Global one and is treated as a completely separate variable.

You need masterCounter to be declared in a higher scope than either of the two functions so that both can have access to it, or you could pass masterCounter to the getEventsWithTime function as an argument.

// By declaring the variable in a higher scope than either of the functions
// that need access to it, both can find it and use it.
let masterCounter = null // later turns into an array

let timeKeyAdditionCheck = (event, range, masterCounter) => {
    assert((range == 'minute' || range == 'hour'), "In calcArrayof... range value needs to equal 'minute' or 'hours'")
    
    if (masterCounter == null) {
        masterCounter = [{timerange: event.timestamp, totalusercount: 0, totalvalidatorcount: 0, totaletherdeposited: 0}]
    }
    if (event.timestamp > (masterCounter[masterCounter.length - 1] + 3599)) {
        
            let differenceInTime = event.timestamp - (masterCounter[masterCounter.length - 1] + 3599);
            let timeKeysNeeded = Math.ceil(differenceInTime / 3600);
        
            i = 0;
            while (i < timeKeysNeeded) {
                
                let newEntry = masterCounter[masterCounter.length - 1];
                newEntry.timerange = newEntry.timerange + 3600;
                masterCounter.push(newEntry);
                i++;
            }
        }
}

let getEventsWithTime = async (firstBlock, lastBlock, range) => {

    try {
        let userTracker = {};
        let events = await depositContract.getPastEvents('DepositEvent', {fromBlock: firstBlock, toBlock: lastBlock}) // 2845084 2846000
    
        for (event of events) {
            let results = await web3.eth.getBlock(event.blockNumber);
            event.timestamp = results.timestamp;

            timeKeyAdditionCheck(event, range, masterCounter);
            checkNewUsers(event, userTracker, masterCounter);
            addValidatorsAndEth(event, userTracker, masterCounter);

        }
        convertToCsv(masterCounter)
        console.log(masterCounter)
        
    
    } catch(error) {
        console.log(error);
    }
}

Also, see this post of mine, which explains the "scope chain" and illustrates this issue in more detail.

Upvotes: 1

Ben
Ben

Reputation: 101

You should declare masterCounter in a scope which contains both functions that are going to use it.

For example, you could declare masterCounter immediately preceding when you declare either function (assuming they are in the same scope).

A sign that your declarations are incorrect is the check to see if masterCounter is null. For it to be shared outside of these functions, it can never be null when the function attempts to access or set it.

Upvotes: 0

Related Questions