dc.jscrossfilter

Conditional custom X Value dim name when no filters are applied to DC.js Chart


Had a follow-up question Original Question which was to group by both color and day, and then find the max day for each color. Gordon did a great job explaining how to keep a running sum for each color per day and find the max. Now I am trying to show the total sum of all colors and their respective peaks in a bar chart.

In short I am looking for a single data point that sums the peak values into a total value and displays Total. I am confused what I should use for the dimension as I want a custom X dim value named TOTAL instead of a list of all of the colors. For the original example a single bar with a value of 25,760 would be displayed for the sum of the peak Color Inventory.

var carData = [
    {Date: "11/26/2020", 'Inventory Region': 'SW', 'Make and Model': 'buick enclave' , 'Inventory Count': 12710, Color: 'charcoal' ,'Color Inventory': 3665},
    {Date: "11/26/2020", 'Inventory Region': 'SW', 'Make and Model': 'chevrolet 1500' , 'Inventory Count': 8510, Color: 'brown', 'Color Inventory': 2520},
    {Date: "11/26/2020", 'Inventory Region': 'NE', 'Make and Model': 'chevrolet camaro', 'Inventory Count': 5250, Color: 'silver', 'Color Inventory': 750},
    {Date: "11/26/2020", 'Inventory Region': 'NW', 'Make and Model': 'chevrolet malibu', 'Inventory Count': 4300, Color: 'brown','Color Inventory': 2100},
    {Date: "11/26/2020", 'Inventory Region': 'NW', 'Make and Model': 'dodge coupe', 'Inventory Count': 15100, Color: 'silver', 'Color Inventory': 5200},
    {Date: "11/26/2020", 'Inventory Region': 'NE', 'Make and Model': 'jeep compass', 'Inventory Count': 7300, Color: 'blue', 'Color Inventory': 2300},
    {Date: "11/26/2020", 'Inventory Region': 'NE', 'Make and Model': 'kia forte', 'Inventory Count': 4250,Color: 'white', 'Color Inventory': 2200},
    {Date: "11/26/2020", 'Inventory Region': 'SW', 'Make and Model': 'kia sorento', 'Inventory Count': 9450,Color: 'red', 'Color Inventory': 6525},
    {Date: "11/27/2020", 'Inventory Region': 'SW', 'Make and Model': 'buick enclave' , 'Inventory Count': 11251, Color: 'charcoal' ,'Color Inventory': 2206},
    {Date: "11/27/2020", 'Inventory Region': 'SW', 'Make and Model': 'chevrolet 1500' , 'Inventory Count': 8246, Color: 'brown', 'Color Inventory': 2256},
    {Date: "11/27/2020", 'Inventory Region': 'NE', 'Make and Model': 'chevrolet camaro', 'Inventory Count': 5200, Color: 'silver', 'Color Inventory': 700},
    {Date: "11/27/2020", 'Inventory Region': 'NW', 'Make and Model': 'chevrolet malibu', 'Inventory Count': 4250, Color: 'brown','Color Inventory': 2050},
    {Date: "11/27/2020", 'Inventory Region': 'NW', 'Make and Model': 'dodge coupe', 'Inventory Count': 15000, Color: 'silver', 'Color Inventory': 5100},
    {Date: "11/27/2020", 'Inventory Region': 'NE', 'Make and Model': 'jeep compass', 'Inventory Count': 7200, Color: 'blue', 'Color Inventory': 2200},
    {Date: "11/27/2020", 'Inventory Region': 'NE', 'Make and Model': 'kia forte', 'Inventory Count': 4150,Color: 'white', 'Color Inventory': 2100},
    {Date: "11/27/2020", 'Inventory Region': 'SW', 'Make and Model': 'kia sorento', 'Inventory Count': 8953,Color: 'red', 'Color Inventory': 6058}
];

Here is the group reduce function from the previous answer:

const cf = crossfilter(carData),
  colorDimension = cf.dimension(({Color}) => Color),
  colorDayGroup = colorDimension.group().reduce(
    (p, v) => { // add
      const day = d3.timeDay(v.Date).getTime(); // 2. round to day, convert to int
      p[day] = (p[day] || 0) + v['Color Inventory']; // 3
      return p;
    },
    (p, v) => { // remove
      const day = d3.timeDay(v.Date).getTime(); // round to day, convert to int
      p[day] = (p[day] || 0) - v['Color Inventory']; // 4
      return p;
    },
    () => ({}) // 1. init
  );

and the value accessor which calculates the max:

chart.valueAccessor(({key, value}) => d3.max(Object.values(value)))

Solution

  • If you want a single value to supply to the dc.js NumberDisplay widget, the best way to get it in there is to supply a fake groupAll object, that is, an object with the single method .value(), which returns the value.

    Here, we want it to take the sum of the max value from each bin:

    function sum_peaks_groupAll(group) {
      return {
        value: () => d3.sum(group.all(), ({value}) => d3.max(Object.values(value)))
      };
    }
    const sumPeaks = sum_peaks_groupAll(colorDayGroup);
    

    For historic reasons, the number display widget requires passing the identity function as value accessor when using a groupAll object for its group:

    number.group(sumPeaks)
      .valueAccessor(d => d);
    

    Demo fiddle.

    Bar chart with single bar

    If you want a bar chart displaying this sum, then you want an ordinary fake group. It's the same calculation but a method named .all() that returns an array with a single element which is a key/value pair:

    function sum_peaks_group(group) {
      return {
        all: () => ([{
          key: 'All',
          value: d3.sum(group.all(), ({value}) => d3.max(Object.values(value)))
        }])
      };
    }