Exact change strange behaviour in loops

Exact change strange behaviour in loops
0

#1

HI folks, been working on this a while (on and off), hoping that the answer will come to me. If I use this code, the amount of change is right, but the following reduce method (which I found on StackOverflow), doesn’t add them in the correct way.

The weird thing is, when I test the reduce method as a standalone function with the same output from the checkCashRegister function, it works. I’m stumped. Anyone have any ideas? The standalone function is below for reference. there is probably a simple answer to this . . . .

function checkCashRegister(price, cash, cid) {
    var change = cash - price;
    var cashTotal = 0;
    var coinsReturned = [];
    var denom = [
        ['ONE HUNDRED', 100],
        ['TWENTY', 20],
        ['TEN', 10],
        ['FIVE', 5],
        ['ONE', 1],
        ['QUARTER', 0.25],
        ['DIME', 0.1],
        ['NICKEL', 0.05],
        ['PENNY', 0.01]
    ];

    var till = [];
    var denomStart;
    cid.forEach(function(item) {
        till.push(item[1].toFixed(2));
    });
    till.reverse();
    (function() {

        for (var i = 0; i < till.length; i++) {
            cashTotal += till[i][1];
        }
        return cashTotal = cashTotal;
    })();

   if (change > cashTotal) {
        return 'Insufficient funds';
      
    } else if (change === cashTotal) {
        return 'Closed';
    } else {
      
        for (var x = 0; x < denom.length; x++) {
            if (change === 0) {
                break;
            } else if (change < denom[x][1]) {
                continue;
            } else if (change > denom[x][1]) {
                change -= denom[x][1];
                till[x] -= denom[x][1];
                coinsReturned.push(denom[x]);
                if (till[x] === 0) {
                    continue;
                } else {
                    x--;
                }
            }
        }

         //reduce method
        var duplicateIndex = {};
        var outputArr = [];
        for (var i = 0; i < coinsReturned.length; i++) {
            var item = coinsReturned[i];
            //var item = holdingCoin[i][0] + "," + holdingCoin[i][1].toFixed(2); 
            var collisionIndex = duplicateIndex[item[0]];
            if (collisionIndex > -1) {
                outputArr[collisionIndex][1] += item[1];
            } else {
                outputArr.push(item).toFixed(2);
                duplicateIndex[item[0]] = outputArr.length - 1;
            }
        }
        return outputArr;
    }
}

checkCashRegister(3.26, 100.00, [
    ["PENNY", 1.01],
    ["NICKEL", 2.05],
    ["DIME", 3.10],
    ["QUARTER", 4.25],
    ["ONE", 90.00],
    ["FIVE", 55.00],
    ["TEN", 20.00],
    ["TWENTY", 60.00],
    ["ONE HUNDRED", 100.00]
]);
//end of first function

//standalone test:

var holdingCoin = [
    ["TWENTY", 20],
    ["TWENTY", 20],
    ["TWENTY", 20],
    ["TEN", 10],
    ["TEN", 10],
    ["FIVE", 5],
    ["FIVE", 5],
    ["FIVE", 5],
    ["ONE", 1],
    ["QUARTER", 0.25],
    ["QUARTER", 0.25],
    ["DIME", 0.1],
    ["DIME", 0.1],
    ["PENNY", 0.01],
    ["PENNY", 0.01],
    ["PENNY", 0.01]
];

var findDuplicatesAndSum = function(inptArr) {
    var duplicateIndex = {};
    var outputArr = [];
    for (var i = 0; i < inptArr.length; i++) {
        var item = inptArr[i];
        //    var item = inptArr[i][0] + "," + inptArr[i][1].toFixed(2); 

        //outputArr.push(item);
        var collisionIndex = duplicateIndex[item[0]];
        if (collisionIndex > -1) {
            outputArr[collisionIndex][1] += item[1];
        } else {
            outputArr.push(item).toFixed(2);
            duplicateIndex[item[0]] = outputArr.length - 1;
        }
    }
    return outputArr;
};
findDuplicatesAndSum(holdingCoin);



#2

Hey @aah101,
I didn’t really understand what your problem is. I have run your code with the example test you gave and it seems to merge similar coins together into one field.

What I did notice is the fact that you have an anonymous function inside your function, which is not a good idea.

    till.reverse();
    (function() {

        for (var i = 0; i < till.length; i++) {
            cashTotal += till[i][1];
        }
        return cashTotal = cashTotal;
    })();

I would output that code into a separate function and call it.


#4

Hey Tomer, thanks for getting back, first time on here and realised after posting that I didn’t make the problem clear enough.

If I run the reduce method inside the full function, I get this:

[“TWENTY”, 80],[“TEN”, 20], [“FIVE”, 20], [“ONE”, 1], [“QUARTER”, 0.5],[“DIME”, 0.2], [“PENNY”, 0.04]
which has reduced the output from the first function:

[“TWENTY”, 20],[“TWENTY”, 20],[“TWENTY”, 20],[“TEN”, 10],[“TEN”, 10],[“FIVE”, 5],[“FIVE”, 5],[“FIVE”, 5],[“ONE”, 1],[“QUARTER”, 0.25],[“QUARTER”, 0.25],[“DIME”, 0.1],[“DIME”, 0.1],[“PENNY”, 0.01],[“PENNY”, 0.01],[“PENNY”, 0.01]

But If I run the reduce method on its own using the same output as a parameter, I get this:

[“TWENTY”, 60],[“TEN”, 20],[“FIVE”, 15],[“ONE”, 1],[“QUARTER”, 0.5],[“DIME”, 0.2],[“PENNY”, 0.03]

I’ve run the debugger on it, and can see that in the first example, the reduce method is changing each instance of, say 20, in the coinsReturned array, in the latter, it changes only the first instance.

Anyway, I’m gonna change that anonymous function first and see if that changes anything. thanks for the advice.


#5

One problem which is causing trouble is the first line inside your for loop:

var item = coinsReturned[i];

In the first iteration of the for loop, the else block of code pushes item into output.

In the second iteration of the for loop, the if evaluates to true, so this line executes:

 outputArr[collisionIndex][1] += item[1];

I believe the above two lines of code creates a circular reference which ends up changing coinsReturned and inadvertently changes outputArr.

See below where I have modified your if/else with console.log statements, so you can see what ends up happening to coinsReturned and outputArr. It is confusing and I even I do not fully understand how it is changing the first three elements in coinsReturned during the 2nd iteration of the for loop.

        for (var i = 0; i < coinsReturned.length; i++) {
            console.log('i = ' + i);
            var item = coinsReturned[i];
            console.log('item = ' + JSON.stringify(item))
            var collisionIndex = duplicateIndex[item[0]];
            console.log('collisionIndex = ' + collisionIndex);
            if (collisionIndex > -1) {
                console.log('if start coinsReturned ...')
                console.log(coinsReturned);
                outputArr[collisionIndex][1] += item[1];
                console.log('\nif end coinsReturned ...')
                console.log(coinsReturned)     
            } else {
                console.log('else start coinsReturned ...')
                console.log(coinsReturned);
                outputArr.push(item).toFixed(2);
                console.log('\nelse end coinsReturned ...')
                console.log(coinsReturned)                   
                duplicateIndex[item[0]] = outputArr.length - 1;
            }
            console.log() // blank line
            console.log('outputArr is below')
            console.log(JSON.stringify(outputArr))
            console.log('\n'); // two blank lines
        }

Below is the results of the first three iterations of the above code:

i = 0
item = [“TWENTY”,20]
collisionIndex = undefined
else start coinsReturned …
[ [ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

else end coinsReturned …
[ [ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

outputArr is below
[[“TWENTY”,20]]

i = 1
item = [“TWENTY”,20]
collisionIndex = 0
if start coinsReturned …
[ [ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TWENTY’, 20 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

if end coinsReturned …
[ [ ‘TWENTY’, 40 ],
[ ‘TWENTY’, 40 ],
[ ‘TWENTY’, 40 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

outputArr is below
[[“TWENTY”,40]]

i = 2
item = [“TWENTY”,40]
collisionIndex = 0
if start coinsReturned …
[ [ ‘TWENTY’, 40 ],
[ ‘TWENTY’, 40 ],
[ ‘TWENTY’, 40 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

if end coinsReturned …
[ [ ‘TWENTY’, 80 ],
[ ‘TWENTY’, 80 ],
[ ‘TWENTY’, 80 ],
[ ‘TEN’, 10 ],
[ ‘TEN’, 10 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘FIVE’, 5 ],
[ ‘ONE’, 1 ],
[ ‘QUARTER’, 0.25 ],
[ ‘QUARTER’, 0.25 ],
[ ‘DIME’, 0.1 ],
[ ‘DIME’, 0.1 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ],
[ ‘PENNY’, 0.01 ] ]

outputArr is below
[[“TWENTY”,80]]


#6

Hi Randell, thanks for doing that. It’s a great tip for next time I get stuck.

I did sort of arrive at that conclusion about those two lines:

var item = coinsReturned[i];
 outputArr[collisionIndex][1] += item[1];

… through watching what happens to each array at each stage of the loop in the debugger. The ‘weird’ thing is that the exact same output used as a parameter in a standalone function works.

I even tried declaring all the variables globally, including the function to replicate it.

So maybe I’m gonna try to find another way of reducing the duplicates in the array and will come back when done. Appreciate very much the help.