Search code examples
javascriptnode.jsdictionaryecmascript-6normalize

Normalize values for all keys in nested dictionaries and append as new key/value pairs


Say I have a dictionary with nested sub-dictionaries:

let dict = 
{
    "SEATTLE" :     {
        "gross_sales" : 106766,
        "price" : 584.50,
        "dates" : [             {
                "date" : "2020-03-13",
                "total_sales_to_date" : 2,
                "new_sales" : 2,
            }
,           {
                "date" : "2020-03-19",
                "total_sales_to_date" : 5,
                "new_sales" : 3,
            }
 ]
    }
,   
    "PHOENIX" :     {
        "gross_sales" : 26691.5,
        "price" : 292.25,
        "dates" : [             {
                "date" : "2020-03-13",
                "total_sales_to_date" : 9,
                "new_sales" : 9,
            }
,           {
                "date" : "2020-03-19",
                "total_sales_to_date" : 19,
                "new_sales" : 10,
            }
 ]
    }

}

And I would like to normalise each numerical value in the key/value pairs against the other key/values and then append these as new key/value pairs.

For the dates array of time-series data I'd like to normalise each key/value pair in each date against both time (within the array) and against the other locations on the same date (other objects).

For example, this is what I'm seeking after the operation:

{
    "SEATTLE" :     {
        "gross_sales" : 106766,
        "normalised_gross_sales" : 1.0,
        "price" : 584.50,
        "normalised_price" : 1.0, 
        "dates" : [             {
                "date" : "2020-03-13",
                "total_sales_to_date" : 2,
                "norm_total_sales_over_time" : 0.4, 
                "norm_total_sales_over_locations" : 0.22222222, 
                "new_sales" : 2,
            }
,           {
                "date" : "2020-03-19",
                "total_sales_to_date" : 5,
                "norm_total_sales_over_time" : 1.0, 
                "norm_total_sales_over_locations" : 0.26315789, 
                "new_sales" : 3,
            }
 ]
    }
,   
    "PHOENIX" :     {
        "gross_sales" : 26691.5,
        "normalised_gross_sales" : 0.25,
        "price" : 292.25,
        "normalised_price" : 0.5,  
        "dates" : [             {
                "date" : "2020-03-13",
                "total_sales_to_date" : 9,
                "norm_total_sales_over_time" : 0.47368421, 
                "norm_total_sales_over_locations" : 1.0, 
                "new_sales" : 9,
            }
,           {
                "date" : "2020-03-19",
                "total_sales_to_date" : 19,
                "norm_total_sales_over_time" : 1.0, 
                "norm_total_sales_over_locations" : 1.0, 
                "new_sales" : 10,
            }
 ]
    }

}

ie: the total_sales_to_date value for the last date in the array should normalise to 1.0 as norm_total_sales_over_time

and the largest total_sales_to_date value for all objects (SEATTLE, PHOENIX) for the current date in the array should normalise to 1.0 as norm_total_sales_over_locations

I'm finding this very complex to handle in JS. My actual task involves dictionaries with hundreds of sub-dictionaries I need to compare, I'm looking for a scalable solution. In a pandas dataframe this would be trivial, however I'd like to learn how to approach this using modern javascript only as I'm running this process from node.js using an ES6 interpreter.

What is an effective ES6 javascript solution to this?


Solution

  • Here is a solution that returns the normalised values in the manner described:

    let dict = {
      "SEATTLE": {
        "gross_sales": 106766,
        "price": 584.50,
        "dates": [{
          "date": "2020-03-13",
          "total_sales_to_date": 2,
          "new_sales": 2,
        }, {
          "date": "2020-03-19",
          "total_sales_to_date": 5,
          "new_sales": 3,
        }]
      },
      "PHOENIX": {
        "gross_sales": 26691.5,
        "price": 292.25,
        "dates": [{
          "date": "2020-03-13",
          "total_sales_to_date": 9,
          "new_sales": 9,
        }, {
          "date": "2020-03-19",
          "total_sales_to_date": 19,
          "new_sales": 10,
        }]
      }
    
    }
    
    
    async function normaliseDict(_dict) {
    
      let values = await Object.values(_dict);
      // make arrays with values from each key
      let all_gross_sales = [];
      let all_price = [];
      let all_total_sales = {};
    
      values.forEach((element) => {
        all_gross_sales.push(element.gross_sales);
        all_price.push(element.price);
    
        let most_recent_total_sales_value = element.dates[element.dates.length - 1].total_sales_to_date;
    
        element.dates.forEach((date, idx) => {
          date.norm_total_sales_over_time = date.total_sales_to_date / most_recent_total_sales_value;
          if (all_total_sales[date.date]) all_total_sales[date.date].push(date.total_sales_to_date);
          else {
            all_total_sales[date.date] = [];
            all_total_sales[date.date].push(date.total_sales_to_date);
          }
        });
    
      });
    
      const newDict = values.map(ob => {
        ob.gross_sales_norm = ob.gross_sales / Math.max(...all_gross_sales);
        ob.price_norm = ob.price / Math.max(...all_price);
        return ob;
      });
    
      values.forEach((element) => {
        element.dates.forEach((date, idx) => {
          date.norm_total_sales_over_locations_for_this_date = date.total_sales_to_date / Math.max(...all_total_sales[date.date]);
        });
      });
      return await dict;
    }
    
    
    (async () => {
      console.log(await normaliseDict(dict))
    })()