jqueryobjectrefactoringeachjquery-csv

Refactoring jQuery each loop on data object


I have a csv file converted to a jQuery object using jQuery CSV (https://github.com/evanplaice/jquery-csv).

Here is the code for that:

    $.ajax({
        type: "GET",
        url: "/path/myfile.csv",
        dataType: "text",
        success: function(data) {
        // once loaded, parse the file and split out into data objects
        // we are using jQuery CSV to do this (https://code.google.com/p/jquery-csv/)

        var data = $.csv.toObjects(data);
    });

I'm adding up the bushels_per_day values by company and want to refactor my code to make it more compact.

Using this answer: Sum values in jQuery object by key, I am able to loop through using $.each();

The object format is like so:

    var data = [
        "0":{
            bushels_per_day: "145",
            plant_city: "Decatur",
            plant_company: "AGP",
        },
        "1":{
            bushels_per_day: "125",
            plant_city: "Cedar Rapids",
            plant_company: "AGP",
        },
        "2":{
            bushels_per_day: "345",
            plant_city: "Ralston",
            plant_company: "AGP",
        },
        "3":{
            bushels_per_day: "55",
            plant_city: "Dawson",
            plant_company: "ADM",
        },
        "4":{
            bushels_per_day: "55",
            plant_city: "Dawson",
            plant_company: "ADM",
        },
        // ... more objects
    ]

And here is the $.each() loop:

    var sumADM = 0;
    var sumAGP = 0;
    // var for each company



    $.each(data, function (index, value) {
        var capacity = parseInt(value.bushels_per_day, 10);
        var company = value.plant_company.replace(/\W+/g, '_').toLowerCase();


        if (company == 'adm') {
            sumADM += capacity;
        }
        if (company == 'agp') {
            sumAGP += capacity;
        }
        // ... and so on for each company
    });

    console.log(sumADM, sumAGP); // and so on.

This works, but how can I refactor this so that I don't need a sum variable and if statement for each company? Currently the sum variable and console.log() must be outside the loop in order to return the correct totals.

Is there a better, more compact way to do this?


Solution

  • You could put the sums on an object as properties:

    var sums = {
        ADM: 0,
        AGP: 0
    };
    
    $.each(data, function (index, value) {
        var capacity = parseInt(value.bushels_per_day, 10);
        var company = value.plant_company.replace(/\W+/g, '_').toUpperCase(); // Note change here
    
        sums[company] += capacity;
    });
    
    console.log(sums.ADM, sums.AGP); // and so on.
    

    Or to output them in a loop:

    Object.keys(sums).forEach(function(company) {
        console.log(sums[company]);
    });
    

    You could even do lazy-init, if the companies vary:

    var sums = {};
    
    $.each(data, function (index, value) {
        var capacity = parseInt(value.bushels_per_day, 10);
        var company = value.plant_company.replace(/\W+/g, '_').toUpperCase();
    
        sums[company] = (sums[company] || 0) + capacity;
    });
    
    Object.keys(sums).forEach(function(company) {
        console.log(sums[company]);
    });
    

    The way the sums[company] = (sums[company] || 0) + capacity; line works is that if we haven't seen that company before, sums[company] will be undefined. Since undefined is falsey, JavaScript's curiously-powerful || operator will take the right-hand operand value (0) as its result. This is also true if we have seen company before and sums[company] is 0, but that's okay, a 0 is a 0. All other values (1 and such) are truthy, so sum[company] || 0 will be 1 and such (the value of the left-hand operand).


    Side note: Note I'm using toUpperCase rather than toLowerCase on the company strings, so they match the properties.