# Data Operation Helpers

Data Query exports individual helper functions for each data operation. The `process` function combines all of them in a single call.

## Sorting

To sort an array by one or more fields, use [`orderBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/orderby). The function accepts a data array and one or more [`SortDescriptor`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/sortdescriptor) objects.

The code snippet below demonstrates basic sorting:

```ts Function Usage
import { orderBy, SortDescriptor } from '@progress/kendo-data-query';

const sort: SortDescriptor[] = [
  { field: 'department', dir: 'asc' },
  { field: 'salary', dir: 'desc' }
];

const sorted = orderBy(employees, sort);
/*
Output:
[
  { id: 3, department: 'Engineering', salary: 105000, ... },
  { id: 1, department: 'Engineering', salary: 95000, ... },
  { id: 5, department: 'Engineering', salary: 88000, ... },
  { id: 2, department: 'Marketing', salary: 72000, ... },
  { id: 4, department: 'Marketing', salary: 68000, ... },
]
*/
```

```js Sample Data
const employees = [
  { id: 1, name: 'Alice Chen', department: 'Engineering', salary: 95000, hireDate: '2019-03-15' },
  { id: 2, name: 'Bob Martinez', department: 'Marketing', salary: 72000, hireDate: '2020-07-01' },
  { id: 3, name: 'Carol Johnson', department: 'Engineering', salary: 105000, hireDate: '2018-11-20' },
  { id: 4, name: 'David Kim', department: 'Marketing', salary: 68000, hireDate: '2021-02-14' },
  { id: 5, name: 'Eva Rodriguez', department: 'Engineering', salary: 88000, hireDate: '2022-05-09' }
];
```

### Custom Comparators

You can provide a custom comparator function for a field to override the default comparison behavior. The comparator receives two items and must return a negative number, zero, or a positive number.

Use this when the natural sort order of the stored values does not match the intended display order — for example, when a field holds a severity string and you need `critical` before `high` before `medium` before `low`.

```ts Custom Comparator
import { orderBy, SortDescriptor } from '@progress/kendo-data-query';

const SEVERITY_RANK: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3 };

const descriptor: SortDescriptor[] = [{
  field: 'severity',
  compare: (a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]
}];

const sorted = orderBy(incidents, descriptor);
/*
Output:
[
  { id: 'P-103', title: 'Data loss on submit', severity: 'critical' },
  { id: 'P-101', title: 'Login timeout', severity: 'high' },
  { id: 'P-104', title: 'Missing pagination', severity: 'high' },
  { id: 'P-102', title: 'Slow report export', severity: 'medium' },
  { id: 'P-105', title: 'Tooltip misaligned', severity: 'low' }
]
*/
```

```js Sample Data
const incidents = [
  { id: 'P-101', title: 'Login timeout', severity: 'high' },
  { id: 'P-102', title: 'Slow report export', severity: 'medium' },
  { id: 'P-103', title: 'Data loss on submit', severity: 'critical' },
  { id: 'P-104', title: 'Missing pagination', severity: 'high' },
  { id: 'P-105', title: 'Tooltip misaligned', severity: 'low' }
];
```

## Filtering

To filter an array, use [`filterBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/filterby). The function accepts a data array as the first argument and a filter descriptor object as the second argument — either a single [`FilterDescriptor`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/filterdescriptor) or a [`CompositeFilterDescriptor`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/compositefilterdescriptor) for multiple conditions. Composite filters can be nested to any depth.

The code snippet below demonstrates a composite filter that returns only shipped orders above a minimum value:

```ts Function Usage
import { filterBy, CompositeFilterDescriptor } from '@progress/kendo-data-query';

const filter: CompositeFilterDescriptor = {
  logic: 'and',
  filters: [
    { field: 'status', operator: 'eq', value: 'Shipped' },
    { field: 'total', operator: 'gte', value: 500 }
  ]
};

const filtered: object[] = filterBy(orders, filter);

/*
Output:
[
  { "customer": "Acme Corp", "status": "Shipped", "total": 1250.00, ... },
  { "customer": "Initech LLC", "status": "Shipped", "total": 895.75, ... }
]
*/
```

```js Sample Data
const orders = [
  { orderId: 1051, customer: 'Acme Corp', status: 'Shipped', total: 1250.00, region: 'West' },
  { orderId: 1052, customer: 'Globex Inc', status: 'Pending', total: 340.50, region: 'East' },
  { orderId: 1053, customer: 'Initech LLC', status: 'Shipped', total: 895.75, region: 'West' },
  { orderId: 1054, customer: 'Umbrella Corp', status: 'Cancelled', total: 2100.00, region: 'South' },
  { orderId: 1055, customer: 'Soylent Corp', status: 'Pending', total: 175.20, region: 'East' }
];
```

The following table lists the supported operators and the types they apply to:

| Operator                                               | Applies to     |
| ------------------------------------------------------ | -------------- |
| `eq`, `neq`                                            | All types      |
| `isnull`, `isnotnull`                                  | All types      |
| `lt`, `lte`, `gt`, `gte`                               | Numbers, dates |
| `startswith`, `endswith`, `contains`, `doesnotcontain` | Strings        |
| `isempty`, `isnotempty`                                | Strings        |

## Grouping

To split an array into groups, use [`groupBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/groupby). The function accepts a data array as the first argument and an array of [`GroupDescriptor`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/groupdescriptor) objects as the second argument.

> Note: `groupBy` does not apply the sort direction specified in a `GroupDescriptor`. Pass `group` descriptors through `process` instead when you need sorted groups.

The code snippet below demonstrates grouping orders by sales region:

```ts Function Usage
import { groupBy, GroupDescriptor, GroupResult } from '@progress/kendo-data-query';

const group: GroupDescriptor[] = [{ field: 'region' }];

const grouped: GroupResult[] = groupBy(orders, group) as GroupResult[];
/*
Output:
[
  {
    "field": "region", "value": "West", "aggregates": {},
    "items": [
      { "orderId": 1051, "customer": "Acme Corp", "region": "West", "total": 1250.00 },
      { "orderId": 1053, "customer": "Initech LLC", "region": "West", "total": 895.75 }
    ]
  },
  { "field": "region", "value": "East", "aggregates": {}, "items": [ ... ] },
  { "field": "region", "value": "South", "aggregates": {}, "items": [ ... ] }
]
*/
```

```js Sample Data
const orders = [
  { orderId: 1051, customer: 'Acme Corp', region: 'West', total: 1250.00 },
  { orderId: 1052, customer: 'Globex Inc', region: 'East', total: 340.50 },
  { orderId: 1053, customer: 'Initech LLC', region: 'West', total: 895.75 },
  { orderId: 1054, customer: 'Umbrella Corp', region: 'South', total: 2100.00 },
  { orderId: 1055, customer: 'Soylent Corp', region: 'East', total: 175.20 }
];
```

## Aggregates

To compute aggregate values over an array, use [`aggregateBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/aggregateby). The function accepts a data array as the first argument and an array of [`AggregateDescriptor`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/aggregatedescriptor) objects as the second argument.

Supported aggregate functions are `sum`, `average`, `count`, `min`, `max`.

The code snippet below demonstrates computing team-wide sales metrics from individual sales rep records:

```ts Function Usage
import { aggregateBy, AggregateDescriptor, AggregateResult } from '@progress/kendo-data-query';

const aggregates: AggregateDescriptor[] = [
  { field: 'rep', aggregate: 'count' },
  { field: 'revenue', aggregate: 'sum' },
  { field: 'revenue', aggregate: 'average' },
];

const result: AggregateResult = aggregateBy(sales, aggregates);
/*
Output:
{
  "rep": { "count": 5 },
  "revenue": { "sum": 620550, "average": 124110 },
}
*/
```

```js Sample Data
const sales = [
  { rep: 'Alice Chen', region: 'West', revenue: 142500, deals: 18, quota: 120000 },
  { rep: 'Bob Martinez', region: 'East', revenue: 98750, deals: 12, quota: 100000 },
  { rep: 'Carol Johnson', region: 'West', revenue: 187300, deals: 24, quota: 150000 },
  { rep: 'David Kim', region: 'East', revenue: 115600, deals: 15, quota: 100000 },
  { rep: 'Eva Rodriguez', region: 'South', revenue: 76400, deals: 9, quota: 90000 }
];
```

## Combined Operations with `process`

The [`process`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/process) function applies sort, filter, group, and aggregate operations simultaneously. The function accepts a data array as the first argument and a [`State`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/state) object as the second argument, and returns a [`DataResult`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/dataresult) object.

The code snippet below demonstrates how to filter, sort, and group a product catalog in a single call:

```ts Function Usage
import { process, State, DataResult } from '@progress/kendo-data-query';

const state: State = {
  group: [{ field: 'category', aggregates: [
    { aggregate: 'sum', field: 'price' },
    { aggregate: 'sum', field: 'stock' }
  ]}],
  sort: [{ field: 'price', dir: 'desc' }],
  filter: { logic: 'or', filters: [
    { field: 'discontinued', operator: 'eq', value: true },
    { field: 'price', operator: 'lt', value: 50 }
  ]}
};

const result: DataResult = process(products, state);
/*
Output:
{
  "data": [
    { "field": "category", "value": "Accessories", "aggregates": { "price": { "sum": 78 }, "stock": { "sum": 150 } }, "items": [ ... ] },
    { "field": "category", "value": "Electronics", "aggregates": { "price": { "sum": 19 }, "stock": { "sum": 200 } }, "items": [ ... ] }
  ],
  "total": 3
}
*/
```

```js Sample Data
const products = [
  { name: 'Laptop Pro 15', category: 'Electronics', price: 1299, stock: 42, discontinued: false },
  { name: 'USB Power Adapter', category: 'Electronics', price: 19, stock: 200, discontinued: false },
  { name: 'Wireless Mouse', category: 'Accessories', price: 29, stock: 150, discontinued: false },
  { name: 'USB-C Hub', category: 'Accessories', price: 49, stock: 0, discontinued: true },
  { name: 'Monitor 27"', category: 'Electronics', price: 449, stock: 18, discontinued: false },
  { name: 'Keyboard Pro', category: 'Accessories', price: 89, stock: 75, discontinued: false }
];
```

## Distinct Values

To extract unique values for a field from an array, use [`distinct`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/distinct). The function accepts a data array and a field name or a custom [`Comparer`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/comparer) function as the second argument, and returns an array containing only the first occurrence of each unique value.

This is useful for populating filter dropdowns from the current dataset.

The code snippet below demonstrates extracting distinct product categories:

```js Function Usage
import { distinct } from '@progress/kendo-data-query';

const categories = distinct(products, 'category');
/*
Output:
[
  { name: 'Laptop Pro 15', category: 'Electronics', ... },
  { name: 'Wireless Mouse', category: 'Accessories', ... }
]
*/
```

## See Also

* [Data Query Overview](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery)
* [Installation](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/get-started)
* [`process`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/process)
* [`orderBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/orderby)
* [`filterBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/filterby)
* [`groupBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/groupby)
* [`aggregateBy`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/aggregateby)
* [`distinct`](https://www.telerik.com/kendo-vue-ui-develop/components/dataquery/api/distinct)
