# PointReducer

```ts
import { PointReducer } from 'cx/charts';
```



`PointReducer` calculates aggregate values from points displayed on the chart. It provides three callback hooks for initializing an accumulator, processing each point, and computing final results.

```tsx
import { Svg } from "cx/svg";
import {
  Chart,
  NumericAxis,
  Gridlines,
  Marker,
  MarkerLine,
  PointReducer,
} from "cx/charts";
import { createModel } from "cx/data";
import { Repeater, Controller } from "cx/ui";
import { Button } from "cx/widgets";

interface Point {
  x: number;
  y: number;
  size: number;
  color: number;
}

interface Model {
  points: Point[];
  avgX: number;
  avgY: number;
  $point: Point;
}

const m = createModel<Model>();

class PageController extends Controller {
  onInit() {
    this.generate();
  }

  generate() {
    this.store.set(
      m.points,
      Array.from({ length: 30 }, (_, i) => ({
        x: 30 + Math.random() * 240,
        y: 30 + Math.random() * 240,
        size: 15 + Math.random() * 25,
        color: i % 8,
      })),
    );
  }
}

export default (
  <div controller={PageController}>
    <Svg style="width: 100%; height: 320px">
      <Chart
        margin="30 30 40 50"
        axes={{
          x: { type: NumericAxis, min: 0, max: 300 },
          y: { type: NumericAxis, min: 0, max: 300, vertical: true },
        }}
      >
        <Gridlines />
        <PointReducer
          onInitAccumulator={(acc) => {
            acc.sumX = 0;
            acc.sumY = 0;
            acc.sumSize = 0;
          }}
          onMap={(acc, x, y, name, p) => {
            acc.sumX += x * p.size;
            acc.sumY += y * p.size;
            acc.sumSize += p.size;
          }}
          onReduce={(acc, { store }) => {
            if (acc.sumSize > 0) {
              store.set(m.avgX, acc.sumX / acc.sumSize);
              store.set(m.avgY, acc.sumY / acc.sumSize);
            }
          }}
        >
          <Repeater records={m.points} recordAlias="$point">
            <Marker
              colorIndex={m.$point.color}
              size={m.$point.size}
              x={m.$point.x}
              y={m.$point.y}
              style={{ fillOpacity: 0.6 }}
              draggableX
              draggableY
            />
          </Repeater>
          <MarkerLine x={m.avgX} colorIndex={9} />
          <MarkerLine y={m.avgY} colorIndex={9} />
        </PointReducer>
      </Chart>
    </Svg>
    <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px">
      <Button onClick="generate">Generate</Button>
      <span style="color: #666">
        Drag markers to see weighted average update
      </span>
    </div>
  </div>
);

```

## How It Works

1. `onInitAccumulator` - Initialize the accumulator object before processing
2. `onMap` - Called for each point with `(accumulator, x, y, name, data, array, index)`
3. `onReduce` - Calculate final values and write them to the store

## Predefined Variants

CxJS provides specialized point reducers for common use cases:

- [MinMaxFinder](/docs/charts/min-max-finder) - Find minimum and maximum values
- [SnapPointFinder](/docs/charts/snap-point-finder) - Snap to nearest data point
- [ValueAtFinder](/docs/charts/value-at-finder) - Track values on line graphs

## Configuration

| Property              | Type       | Description                                                                        |
| --------------------- | ---------- | ---------------------------------------------------------------------------------- |
| `onInitAccumulator`   | `function` | Initialize accumulator. Args: `(accumulator, instance)`                            |
| `onMap`               | `function` | Process each point. Args: `(accumulator, x, y, name, data, array, index)`          |
| `onReduce`            | `function` | Calculate final values. Args: `(accumulator, instance)`                            |
| `filterParams`        | `object`   | Parameters passed to `onCreatePointFilter`.                                        |
| `onCreatePointFilter` | `function` | Create a predicate for filtering points. Args: `(filterParams, instance)`          |