Search code examples
javascriptrxjs

how to find max min from nested typed object array using RxJS?


given I have this

type DataTableType = {
  name: string;
  series: {
    name: string;
    value: number;
  }[];
}[];

let datatable: DataTableType = [
    {
      name: 'Row 1',
      series: [
        {
          name:'Series 1',
          value:7110
        },
        {
          name:'Series 2',
          value:8240
        },
        {
          name:'Series 3',
          value:2111
        },
        {
          name:'Series 4',
          value:3120
        }
      ]
    },
    {
      name: 'Row 2',
      series: [
        {
          name:'Series 1',
          value:6500
        },
        {
          name:'Series 2',
          value:9120
        },
        {
          name:'Series 3',
          value:5123
        },
        {
          name:'Series 4',
          value:8200
        }
      ]
    },
    {
      name: 'Row 3',
      series: [
        {
          name:'Series 1',
          value:-6453
        },
        {
          name:'Series 2',
          value:-9000
        },
        {
          name:'Series 3',
          value:5009
        },
        {
          name:'Series 4',
          value:8900
        }
      ]
    },
    {
      name: 'Row 4',
      series: [
        {
          name:'Series 1',
          value:8567
        },
        {
          name:'Series 2',
          value:4332
        },
        {
          name:'Series 3',
          value:-3111
        },
        {
          name:'Series 4',
          value:-3222
        }
      ]
    },
    {
      name: 'Row 5',
      series: [
        {
          name:'Series 1',
          value:8333
        },
        {
          name:'Series 2',
          value:3234
        },
        {
          name:'Series 3',
          value:-7323
        },
        {
          name:'Series 4',
          value:1544
        }
      ]
    },
    {
      name: 'Row 6',
      series: [
        {
          name:'Series 1',
          value:2112
        },
        {
          name:'Series 2',
          value:3232
        },
        {
          name:'Series 3',
          value:-8231
        },
        {
          name:'Series 4',
          value:1111
        }
      ]
    },
    {
      name: 'Row 7',
      series: [
        {
          name:'Series 1',
          value:-2112
        },
        {
          name:'Series 2',
          value:3232
        },
        {
          name:'Series 3',
          value:8231
        },
        {
          name:'Series 4',
          value:-9111
        }
      ]
    },
    {
      name: 'Row 8',
      series: [
        {
          name:'Series 1',
          value:-2112
        },
        {
          name:'Series 2',
          value:100
        },
        {
          name:'Series 3',
          value:-3
        },
        {
          name:'Series 4',
          value:0
        }
      ]
    }
  ]

I want to find the Max and Min from the datatable with using RxJS, I have done this below to find the Max and Min

function findMax(column: DataTableType) {
  let maxN = column[0].series[0].value;
  of(...column)
    .pipe(
      mergeMap(
        (item: DataTableType[number], i: number) => from(
          [...item.series]
        )
        .pipe(
          max((a, b) => a.value < b.value? b.value : a.value)
        )
      )
    )
    .subscribe(
      (category: { value: number; }) => {
        maxN = category.value;
      }
    )
  ;
  return maxN;
}

function findMin(column: DataTableType) {
  let minN = column[0].series[0].value;
  of(...column)
    .pipe(
      mergeMap(
        (item: DataTableType[number], i: number) => from(
          [...item.series]
        )
        .pipe(
          min((a, b) => a.value > b.value? b.value : a.value)
        )
      )
    )
    .subscribe(
      (category: { value: number; }) => {
        minN = category.value;
      }
    )
  ;
  return minN;
}

But whenever I run the functions findMax(datatable) and findMin(datatable) I get max = 0 and min = -2112 of the last element of the object array which is Row 8.

My expected answer should be max = 9120 and min = -9111

Please I need help.


Solution

  • The functions given to min and max should behave like the comparator function passed to Array.sort():

    The return value should be a number whose sign indicates the relative order of the two elements: negative if a is less than b, positive if a is greater than b, and zero if they are equal.

    Unfortunately, the documentation of RxJS provides this only through examples, i.e. for min() it gives the example:

    min((a, b) => a.age < b.age ? -1 : 1)
    

    Note that min and max work with the same comparison, you don't have to adjust it depending on whether you want min or max. So in your case, it is (a,b) => a.value - b.value for both.


    A second problem in your functions is that you apply the min/max to the steam you generate inside the mergeMap(), so it will be evaluated for each group, instead of the flattened stream. So you will get a result for every Row item, and your variable will be set to each of them, until it is returned with the value from the last iteration.

    Instead, apply min/max after the merge, so that you get the smallest/largest value from the whole stream.

    Together with the fixed comparator, this gives you:

    function findMax(column) {
      let maxN = column[0].series[0].value;
      from(column)
        .pipe(mergeMap(item => item.series))
        .pipe(max((a, b) => a.value - b.value))
        .subscribe(
          (category) => {
            maxN = category.value;
          }
        )
      ;
      return maxN;
    }
    

    Alternatively, you can also have the mergeMap extract the values directly, then you don't have to use a comparator:

    function findMax(column) {
      let maxN = column[0].series[0].value;
      from(column)
        .pipe(mergeMap(item => from(item.series).pipe(map(s => s.value))))
        .pipe(max())
        .subscribe(
          (category) => {
            maxN = category;
          }
        )
      ;
      return maxN;
    }