export function splitSeriesDataByAccessors()

in packages/charts/src/chart_types/xy_chart/utils/series.ts [117:238]


export function splitSeriesDataByAccessors(
  spec: BasicSeriesSpec,
  xValueSums: Map<string | number, number>,
  insertOrder = 0,
  isStacked = false,
  isBanded = false,
  stackMode?: StackMode,
  groupBySpec?: SmallMultiplesGroupBy,
  deselectedDataSeries: SeriesIdentifier[] = [],
): {
  dataSeries: Map<SeriesKey, DataSeries>;
  xValues: Array<string | number>;
} {
  const {
    seriesType,
    id: specId,
    groupId,
    data,
    xAccessor,
    yAccessors,
    y0Accessors,
    markSizeAccessor,
    splitSeriesAccessors = [],
  } = spec;
  const dataSeries = new Map<SeriesKey, DataSeries>();
  const xValues: Array<string | number> = [];
  const nonNumericValues: Map<unknown, number> = new Map();

  if (isStacked && Boolean(y0Accessors?.length)) {
    Logger.warn(
      `y0Accessors are not allowed with stackAccessors. y0Accessors will be ignored but available under initialY0.`,
    );
  }

  for (let i = 0; i < data.length; i++) {
    const datum = data[i];
    const splitAccessors = getSplitAccessors(datum, splitSeriesAccessors);
    // if splitSeriesAccessors are defined we should have at least one split value to include datum
    if (splitSeriesAccessors.length > 0 && splitAccessors.size < 1) {
      continue;
    }

    // skip if the datum is not an object or null
    if (typeof datum !== 'object' || datum === null) {
      continue;
    }
    const x = getAccessorValue(datum, xAccessor);
    // skip if the x value is not a string or a number
    if (typeof x !== 'string' && typeof x !== 'number') {
      continue;
    }

    xValues.push(x);
    let sum = xValueSums.get(x) ?? 0;

    // extract small multiples aggregation values
    const smH = groupBySpec?.horizontal?.by?.(spec, datum);
    const smV = groupBySpec?.vertical?.by?.(spec, datum);

    const xAccessorStr = getAccessorFieldName(xAccessor, 0);
    yAccessors.forEach((accessor, index) => {
      const cleanedDatum = extractYAndMarkFromDatum(
        datum,
        accessor,
        nonNumericValues,
        isBanded,
        y0Accessors && y0Accessors[index],
        markSizeAccessor,
      );

      const yAccessorStr = getAccessorFieldName(accessor, index);
      const splitAccessorStrs = [...splitAccessors.values()].map((a, si) => getAccessorFieldName(a, si));
      const seriesKeys = [...splitAccessorStrs, yAccessorStr];
      const seriesIdentifier: Omit<XYChartSeriesIdentifier, 'key'> = stripUndefined({
        specId,
        seriesKeys,
        xAccessor: xAccessorStr,
        yAccessor: yAccessorStr,
        splitAccessors,
        smVerticalAccessorValue: smV,
        smHorizontalAccessorValue: smH,
      });
      const seriesKey = getSeriesKey(seriesIdentifier, groupId);
      sum += cleanedDatum.y1 ?? 0;
      const newDatum = { x, ...cleanedDatum, smH, smV };
      const series = dataSeries.get(seriesKey);
      if (series) {
        series.data.push(newDatum);
      } else {
        dataSeries.set(seriesKey, {
          ...seriesIdentifier,
          groupId,
          seriesType,
          stackMode,
          isStacked,
          seriesKeys,
          key: seriesKey,
          data: [newDatum],
          spec,
          insertOrder: insertOrder + dataSeries.size,
          sortOrder: 0,
          isFiltered: deselectedDataSeries.some(({ key: deselectedKey }) => seriesKey === deselectedKey),
        });
      }

      xValueSums.set(x, sum);
    });
  }

  if (nonNumericValues.size > 0) {
    const values = [...nonNumericValues.entries()];
    const total = values.reduce((sum, [, v]) => sum + v, 0);
    Logger.warn(
      `Found ${total} non-numeric y value${total > 1 ? 's' : ''} in dataset for spec "${specId}"`,
      `(${values.map(([k, v]) => `${v}: ${JSON.stringify(k)}`).join(', ')})`,
    );
  }
  return {
    dataSeries,
    xValues,
  };
}