Home > Enterprise >  JS count numbers between
JS count numbers between

Time:05-04

datasets: [
  { label: 'A', data: [82, 160, 10, 40, 179] },
  { label: 'B', data: [101, 180, 80, 85, 119] },
  { label: 'C', data: [124, 160, 99, 130, 43] },
  { label: 'D', data: [66, 153, 82, 81, 97] }
]

what is the best (shortest) way to make data like this:

datasets: [
  { label: 'A', data: [
    {'fits': '180', 'counter': 0},
    {'fits': '160 ', 'counter': 2}, //160, 179
    {'fits': '140 ', 'counter': 0},
    {'fits': '120 ', 'counter': 0},
    {'fits': '100 ', 'counter': 0},
    {'fits': '80 ', 'counter': 1}, //82
    {'fits': '60 ', 'counter': 0},
  ]},
  { label: 'B', data: [
    {'fits': '180', 'counter': 1}, //180
    {'fits': '160 ', 'counter': 0},
    {'fits': '140 ', 'counter': 0},
    {'fits': '120 ', 'counter': 0},
    {'fits': '100 ', 'counter': 2}, //101,119
    {'fits': '80 ', 'counter': 2}, //80,85
    {'fits': '60 ', 'counter': 0},
  ]},
  { label: 'C', data: [
    {'fits': '180', 'counter': 0},
    {'fits': '160 ', 'counter': 1}, //160
    {'fits': '140 ', 'counter': 0},
    {'fits': '120 ', 'counter': 2}, //124, 130
    {'fits': '100 ', 'counter': 0},
    {'fits': '80 ', 'counter': 1}, //99
    {'fits': '60 ', 'counter': 0},
  ]},
  { label: 'D', data: [
    {'fits': '180', 'counter': 0},
    {'fits': '160 ', 'counter': 0},
    {'fits': '140 ', 'counter': 1}, //153
    {'fits': '120 ', 'counter': 0},
    {'fits': '100 ', 'counter': 0},
    {'fits': '80 ', 'counter': 3}, //82,81,97
    {'fits': '60 ', 'counter': 1}, //66
  ]}
]

CodePudding user response:

const datasets = [
    { label: 'A', data: [82, 160, 10, 40, 179] },
    { label: 'B', data: [101, 180, 80, 85, 119] },
    { label: 'C', data: [124, 160, 99, 130, 43] },
    { label: 'D', data: [66, 153, 82, 81, 97] },
];

const handleDatasets = (datasets) => {
    const fits = [180, 160, 140, 120, 100, 80, 60];

    return datasets.map((set) => {
        return {
            ...set,
            data: fits.map((num, i) => {
                return {
                    fits: num,
                    counter: set.data.filter((dataNum) => (dataNum >= num && (fits[i - 1] ? dataNum < fits[i - 1] : true))).length,
                };
            }),
        };
    });
};

console.log(handleDatasets(datasets));

CodePudding user response:

You can simply round them all down to the nearest increment of 20 and group them, finally mapping against your ranges.

const input = [{ label: 'A', data: [82, 160, 10, 40, 179] }, { label: 'B', data: [101, 180, 80, 85, 119] }, { label: 'C', data: [124, 160, 99, 130, 43] }, { label: 'D', data: [66, 153, 82, 81, 97] }]

const fitArr = [180, 160, 140, 120, 100, 80, 60];
const result = input.map(({ data, ...rest }) => {
  const dataMap = data
    .reduce((a, d) => {
      const fit = ~~(d / 20) * 20;
      a[fit] = (a[fit] ?? 0)   1;
      return a
    }, {});

  return {
    ...rest,
    data: fitArr.map(fits => ({ fits, count: dataMap[fits] ?? 0 }))
  }
});

console.log(result);

The nice thing about this approach is it's easily made generic, here accepting an increment and a min and max of the range and returning your expected output.

function mapFitForRange(dataSets, { inc = 1, min = 0, max = 10 }) {
  const fitArr = Array.from({ length: (max - min) / inc   1 }, (_, i) => max - inc * i)

  return dataSets.map(({ data, ...rest }) => {
    const dataMap = data
      .reduce((a, d) => {
        const fit = ~~(d / inc) * inc;
        a[fit] = (a[fit] ?? 0)   1;
        return a
      }, {});

    return {
      ...rest,
      data: fitArr.map(fits => ({ fits, count: dataMap[fits] ?? 0 }))
    }
  });
}

const input = [{ label: 'A', data: [82, 160, 10, 40, 179] }, { label: 'B', data: [101, 180, 80, 85, 119] }, { label: 'C', data: [124, 160, 99, 130, 43] }, { label: 'D', data: [66, 153, 82, 81, 97] }];

console.log(mapFitForRange(input, { inc: 20, min: 60, max: 180 }))

// or change the increment and range
console.log(mapFitForRange(input, { inc: 10, min: 0, max: 100 }))
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row::after { display: none !important; }

  • Related