Home > Blockchain >  Never updated variable has wrong content
Never updated variable has wrong content

Time:04-08

I am pretty confused what happens with my code. The tool I wrote, accepts uploaded CSV files and parses some to get some data. To have a init state for my useres I globally declared emptyUser with default values.

When I run my script and upload a file, emptyUser gets updated with data, which should never happen. When I do not process any data, but output the emptyUser straight away, the object is empty as expected.

The 2nd thing is, that all users have the same data at the end, which is also the same data as in emptyUser

Could someone explain to me why the object gets filled even when I never change the content of it and why all users have the same content as well?

import React from "react";
import {DropzoneArea} from "material-ui-dropzone";


let emptyUser = {
    dealt: {
        totalHull: 0,
        avgHull: 0,

        totalShield: 0,
        avgShield: 0,

        totalMitigated: 0,
        avgMitigated: 0,

        totalDealt: 0,
        avgDealt: 0,

        highest: 0,
        lowest: 9999999999999999999999999999,

        attackCount: 0,
        critCount: 0,
        highestCrit: 0,
        lowestCrit: 0
    },
    received: {
        totalHull: 0,
        avgHull: 0,

        totalShield: 0,
        avgShield: 0,

        totalMitigated: 0,
        avgMitigated: 0,

        totalDealt: 0,
        avgDealt: 0,

        highest: 0,
        lowest: 9999999999999999999999999999,

        attackCount: 0,
        critCount: 0,
    },
    roundDied: 0
}

let attackerList = [];
let users = [];

export class App extends React.Component {


    roundTwoDecimals(num) {
        return Math.round((num   Number.EPSILON) * 100) / 100
    }

    prepareUsers(actions) {
        let users = [];
        actions.forEach(action => {
            let attackerType = action['Angreiferallianz'].trim() === '--' ? 'hostile' : 'user';
            let attacker = action['Angreifername'];

            if (action['Typ'] === 'Angriff' && attackerType === 'user' &&  !attackerList.includes(attacker)) {

                // console.log('empty',emptyUser);  // even this was full of data
                users[attacker] = emptyUser;
                attackerList.push(attacker);
            }
        })
        return users;
    }

    calcBattleData(data, receivedOrDealt, hullDamage, shieldDamage, mitigatedDamage, dealtDamage, isCrit) {
        data[receivedOrDealt] = {
            ...data[receivedOrDealt],
            attackCount: data[receivedOrDealt].attackCount   1,
            totalHull: data[receivedOrDealt].totalHull   hullDamage,
            totalShield: data[receivedOrDealt].totalShield   shieldDamage,
            totalMitigated: data[receivedOrDealt].totalMitigated   mitigatedDamage,
            totalDealt: data[receivedOrDealt].totalDealt   dealtDamage,
            highest: data[receivedOrDealt].highest <= dealtDamage ? dealtDamage : data[receivedOrDealt].highest,
            lowest: data[receivedOrDealt].lowest >= dealtDamage && dealtDamage > 0 ? dealtDamage : data[receivedOrDealt].lowest,
            critCount: isCrit ? data[receivedOrDealt].critCount   1 : data[receivedOrDealt].critCount
        };
        return data;
    }

    parseLog(actions) {
        // let users = this.prepareUsers(actions); // commented out because I suspected that something in this function caused it
        let target = emptyUser;


        let count = 0;
        actions.some(action => {
            if (action['Typ'] === 'Angriff') {
                let attacker = action['Angreifername'];
                let attacked = action['Zielname'];
                let attackerType = action['Angreiferallianz'].trim() === '--' ? 'hostile' : 'user';
                let hullDamage = parseInt(action['Hüllenschaden']);
                let shieldDamage = parseInt(action['Schildschaden']);
                let mitigatedDamage = parseInt(action['Abgemilderter Schaden']);
                let dealtDamage = parseInt(action['Gesamtschaden']);
                let isCrit = action['Kritischer Treffer?'] === 'JA';

                // the attacker is a player - alternative for this.prepareUsers
                if(attackerType === 'user' && !attackerList.includes(attacker)){
                    users[attacker] = emptyUser;
                    attackerList.push(attacker);
                }

                // the attacked one is a player - alternative for this.prepareUsers
                if(attackerType === 'hostile' && !attackerList.includes(attacked)){
                    users[attacked] = emptyUser;
                    attackerList.push(attacked);
                }

                if (attackerType === 'user') {
                    users[attacker] = this.calcBattleData(users[attacker], 'dealt', hullDamage, shieldDamage, mitigatedDamage, dealtDamage, isCrit);
                    target          = this.calcBattleData(target         , 'received', hullDamage, shieldDamage, mitigatedDamage, dealtDamage, isCrit);
                } else {
                    users[attacked] = this.calcBattleData(users[attacked], 'received', hullDamage, shieldDamage, mitigatedDamage, dealtDamage, isCrit);
                    target          = this.calcBattleData(target         , 'dealt', hullDamage, shieldDamage, mitigatedDamage, dealtDamage, isCrit);
                }
            }
            // Kämpfer vernichtet
            count  ;

            // limit it to 20 actions for debugging
            if(count === 20){
                return true;
            }

        })

        Object.keys(users).forEach(user => {
            users[user].dealt.avgHull = this.roundTwoDecimals(users[user].dealt.totalHull / users[user].dealt.attackCount);
            users[user].dealt.avgDealt = this.roundTwoDecimals(users[user].dealt.totalDealt / users[user].dealt.attackCount);
            users[user].dealt.avgShield = this.roundTwoDecimals(users[user].dealt.totalShield / users[user].dealt.attackCount);
            users[user].dealt.avgMitigated = this.roundTwoDecimals(users[user].dealt.totalMitigated / users[user].dealt.attackCount);
        });
        console.log(users);
        console.log(target);
    }

    handleFileUpload(files) {
        if (files && files.length <= 0) {
            return false;
        }

        files.forEach(file => {

            let myFile = file;
            let reader = new FileReader();
            reader.readAsText(myFile, 'UTF-8');

            reader.addEventListener('load', e => {
                console.log(e);
                let csvData = e.target.result;
                csvData = csvData.replaceAll(/Officer,(\d*),(\d*),(\w)/g, 'Officer,$1.$2,$3');
                let csvBlocks = csvData.split("\r\n\r\n");


                csvBlocks.forEach((block, index) => {
                    //console.log(block);
                    if (block === '') return false;
                    let lines = block.split(/\n/g);
                    let headers = lines[0].split(',')
                    let result = [];
                    for (let i = 1; i < lines.length; i  ) {
                        let obj = {};
                        let currentLine = lines[i].split(",");

                        for (let j = 0; j < headers.length; j  ) {
                            obj[headers[j]] = currentLine[j];
                        }

                        result.push(obj);
                    }

                    // process only the fourth block for now
                    if (index === 3) {
                        console.log('parse log');
                        console.log(emptyUser); // already here the object is filled, when I keep in the next line
                        this.parseLog(result); // when I comment this out, empty user has the correct content
                    }
                })

            });
        })
    }

    render() {
        return (
            <>
                <DropzoneArea
                    onChange={(files) => this.handleFileUpload(files)}
                />
            </>

        )
    }
}

CodePudding user response:

Recall that in JavaScript, objects are passed by reference. Quoting MDN docs,

Objects are a reference type. Two distinct objects are never equal, even if they have the same properties.

Whenever you push an object to an array, you're actually pushing its reference and any changes to one of its properties will propagate to all occurrences of that object in the array.

In this case, the same emptyUser is being added to users array in some users[attacker] = emptyUser lines. The immediate fix is to instantiate a new object each time you use emptyUser. Luckily, with the spread syntax (...), you're able to perform a copy of all the properties onto the new reference.

It might be a better way of doing it in terms of refactoring, but changing all the occurrences of raw emptyUser to users[attacker] = {...emptyUser} should solve the problem right off the bat.

  • Related