bucketing
Version:
group an array of items into buckets
127 lines (98 loc) • 3.62 kB
Markdown
# bucketing
In two tiers, group an array of items into buckets. At its core this is not
entirely dissimilar to [`lodash.groupBy`](https://lodash.com/docs/#groupBy),
but with a few differences:
- Two-tiered: `lodash.groupBy` expects one function that takes an item and
generates a key. `bucketing` expects two functions: one that takes an item
and generates a label, and one that takes a label and generates a key. This
allows you to group objects by a complex object rather than strings, working
around the fact that JavaScript objects can only have string or number keys.
- Auxiliary structures: `lodash.groupBy` returns just the resulting grouping.
`bucketing` returns the original items, a unique array of labels, the same
usual buckets, as well as a map from key to label.
This module works with JavaScript as well as TypeScript out of the box.
## Example
Given the following book data:
```js
const mg = {id: 'mg', name: 'Max Gladstone'};
const nm = {id: 'nm', name: 'Neve Maslakovic'};
const ww = {id: 'ww', name: 'Will Wight'};
const gf = {id: 'gf', name: 'Gillian Flynn'};
const books = [
{title: 'Four Roads Cross', author: mg},
{title: 'Gone Girl', author: gf},
{title: 'Soulsmith', author: ww},
{title: 'Regarding Ducks and Universes', author: nm},
{title: 'Two Serpents Rise', author: mg},
{title: 'Sharp Objects', author: gf},
];
```
We can group them by author on author ID:
```js
import {group} from 'bucketing';
const groupings = group(
books,
book => book.author,
author => author.id
);
```
This gives us:
```js
// The original:
groupings.items; // === books
// The labels:
groupings.labels; // equal to [mg, gf, ww, nm]
// The buckets:
groupings.keyToItems;
/*
equal to: {
'mg': [
{title: 'Four Roads Cross', author: mg},
{title: 'Two Serpents Rise', author: mg}
],
'gf': [
{title: 'Gone Girl', author: gf},
{title: 'Sharp Objects', author: gf}
],
'ww': [
{title: 'Soulsmith', author: ww},
],
'nm': [
{title: 'Regarding Ducks and Universes', author: nm}
]
}
*/
// The map:
groupings.keyToLabel;
/*
equal to: {
'mg': {id: 'mg', name: 'Max Gladstone'},
'gf': {id: 'gf', name: 'Gillian Flynn'},
'ww': {id: 'ww', name: 'Will Wight'},
'nm': {id: 'nm', name: 'Neve Maslakovic'}
}
*/
```
## API
### `group<T, L>(items: T[], by: T => L, on: L => string): Grouping`
Takes `items`, buckets them using the labels generated from the `by` function,
and keys those labels using the keys generated from the `on` function. Returns
a `Grouping`. Please ensure that the `on` function generates unique keys for a
given label. That is to say: no two labels should share the same key.
### `Grouping<Item, Label>`
The `Grouping<Item, Label>` type is the return type of `group`. It contains
four things:
- `items: Item[]`: the original array of items passed to `group`
- `labels: Label[]`: a unique array of labels generated from the array of items.
The key generated by the `on` function is used to determine label equality.
- `keyToItems: ItemBuckets<Item>`: a map in which each key-value pair is a
bucket. The key is the key of bucket's label, and the value is an array of
items that all fall under said bucket.
- `keyToLabel: LabelMap<Item>`: a map that maps from key to label.
### `ItemBuckets<Item>`
A type defined as `{ [key: string]: Item[] }`. Conceptually an unordered list
of buckets. Each bucket has a label (whose key is the bucket's key) and
contains one or more items (in the bucket's value).
### `LabelMap<Label>`
A type defined as `{ [key: string]: Label }`. Conceptually a map of keys to
labels.