@tidyjs/tidy 2.6.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/genai-docs/api-core.md +357 -0
- package/genai-docs/api-grouping.md +400 -0
- package/genai-docs/api-joins.md +118 -0
- package/genai-docs/api-other.md +238 -0
- package/genai-docs/api-pivot.md +112 -0
- package/genai-docs/api-selectors.md +159 -0
- package/genai-docs/api-sequences.md +127 -0
- package/genai-docs/api-slice.md +137 -0
- package/genai-docs/api-summarize.md +528 -0
- package/genai-docs/api-vector.md +239 -0
- package/genai-docs/gotchas.md +193 -0
- package/genai-docs/index.md +44 -0
- package/genai-docs/mental-model.md +270 -0
- package/genai-docs/patterns.md +384 -0
- package/genai-docs/quick-reference.md +125 -0
- package/package.json +3 -2
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Grouping API Reference
|
|
2
|
+
|
|
3
|
+
Detailed reference for `groupBy` and its export modes.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { tidy, groupBy, summarize, sum, mean, n } from '@tidyjs/tidy';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- keywords: groupBy, group, nest, aggregate, split-apply-combine, grouped data -->
|
|
12
|
+
## groupBy
|
|
13
|
+
|
|
14
|
+
Groups data by one or more keys, runs tidy functions on each group, and returns results either as a flat array (default) or in a structured export format.
|
|
15
|
+
|
|
16
|
+
**Signature:**
|
|
17
|
+
```ts
|
|
18
|
+
groupBy(
|
|
19
|
+
groupKeys: string | (d: T) => any | Array<string | (d: T) => any>,
|
|
20
|
+
fns: TidyFn | TidyFn[],
|
|
21
|
+
options?: GroupByOptions | ExportShortcut
|
|
22
|
+
)
|
|
23
|
+
```
|
|
24
|
+
**Goes inside:** `tidy()` pipeline
|
|
25
|
+
|
|
26
|
+
### Parameters
|
|
27
|
+
|
|
28
|
+
- **groupKeys** -- what to group by
|
|
29
|
+
- `string` -- a property name: `'category'`
|
|
30
|
+
- `(d) => any` -- an accessor function: `(d) => d.date.getFullYear()`
|
|
31
|
+
- `Array<string | (d) => any>` -- multiple keys: `['category', 'region']`
|
|
32
|
+
- Grouping uses strict equality (`===`). For non-primitive keys, return a primitive from an accessor function.
|
|
33
|
+
- **fns** -- tidy functions to run on each group's subset
|
|
34
|
+
- A single function: `summarize({ total: sum('value') })`
|
|
35
|
+
- An array of functions: `[filter((d) => d.active), summarize({ n: n() })]`
|
|
36
|
+
- **options?** -- controls output shape
|
|
37
|
+
- Omitted or `undefined` -- returns a flat array with group keys merged back in (default)
|
|
38
|
+
- `{ addGroupKeys: false }` -- flat array without merging group keys into results
|
|
39
|
+
- An export shortcut (`groupBy.object()`, `groupBy.entries()`, etc.) -- returns structured output; makes the function a `TidyGroupExportFn` (must be last in the pipeline)
|
|
40
|
+
|
|
41
|
+
### Behavior
|
|
42
|
+
|
|
43
|
+
- Group keys are automatically merged back into each result item (disable with `addGroupKeys: false`).
|
|
44
|
+
- Without an export option, output is a flat `T[]` -- the results from all groups concatenated.
|
|
45
|
+
- With an export option, the return type changes (object, entries, Map, etc.) and the `groupBy` call must be the **last** step in the `tidy()` pipeline.
|
|
46
|
+
|
|
47
|
+
### Example: Default (flat array)
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const data = [
|
|
51
|
+
{ str: 'a', ing: 'x', value: 1 },
|
|
52
|
+
{ str: 'b', ing: 'x', value: 100 },
|
|
53
|
+
{ str: 'a', ing: 'y', value: 2 },
|
|
54
|
+
{ str: 'b', ing: 'y', value: 300 },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
tidy(data, groupBy('str', [summarize({ total: sum('value') })]))
|
|
58
|
+
// output:
|
|
59
|
+
// [
|
|
60
|
+
// { str: 'a', total: 3 },
|
|
61
|
+
// { str: 'b', total: 400 }
|
|
62
|
+
// ]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Example: Multi-key grouping
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
tidy(data, groupBy(['str', 'ing'], [summarize({ total: sum('value') })]))
|
|
69
|
+
// output:
|
|
70
|
+
// [
|
|
71
|
+
// { str: 'a', ing: 'x', total: 1 },
|
|
72
|
+
// { str: 'a', ing: 'y', total: 2 },
|
|
73
|
+
// { str: 'b', ing: 'x', total: 100 },
|
|
74
|
+
// { str: 'b', ing: 'y', total: 300 }
|
|
75
|
+
// ]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Example: Accessor function as key
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
tidy(
|
|
82
|
+
data,
|
|
83
|
+
groupBy(
|
|
84
|
+
(d) => (d.value >= 100 ? 'high' : 'low'),
|
|
85
|
+
[summarize({ n: n() })]
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
// output:
|
|
89
|
+
// [{ n: 2 }, { n: 2 }]
|
|
90
|
+
// Note: accessor-function keys are not merged back (no property name to use).
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
<!-- keywords: export, entries, nested, key-value pairs -->
|
|
96
|
+
## groupBy.entries()
|
|
97
|
+
|
|
98
|
+
Returns `[[key, values], ...]` -- an array of `[key, nestedEntriesOrLeafArray]` tuples.
|
|
99
|
+
|
|
100
|
+
**Signature:** `groupBy.entries(options?: ExportOptions)`
|
|
101
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
102
|
+
|
|
103
|
+
### Example
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
tidy(
|
|
107
|
+
data,
|
|
108
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.entries())
|
|
109
|
+
)
|
|
110
|
+
// output:
|
|
111
|
+
// [
|
|
112
|
+
// ['a', [{ str: 'a', total: 3 }]],
|
|
113
|
+
// ['b', [{ str: 'b', total: 400 }]]
|
|
114
|
+
// ]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
With `single: true` (unwraps single-item leaf arrays):
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
tidy(
|
|
121
|
+
data,
|
|
122
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.entries({ single: true }))
|
|
123
|
+
)
|
|
124
|
+
// output:
|
|
125
|
+
// [
|
|
126
|
+
// ['a', { str: 'a', total: 3 }],
|
|
127
|
+
// ['b', { str: 'b', total: 400 }]
|
|
128
|
+
// ]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
<!-- keywords: export, entriesObject, key values object -->
|
|
134
|
+
## groupBy.entriesObject()
|
|
135
|
+
|
|
136
|
+
Returns `[{ key, values }, ...]` -- like `entries()` but as objects instead of tuples.
|
|
137
|
+
|
|
138
|
+
**Signature:** `groupBy.entriesObject(options?: ExportOptions)`
|
|
139
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
140
|
+
|
|
141
|
+
### Example
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
tidy(
|
|
145
|
+
data,
|
|
146
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.entriesObject())
|
|
147
|
+
)
|
|
148
|
+
// output:
|
|
149
|
+
// [
|
|
150
|
+
// { key: 'a', values: [{ str: 'a', total: 3 }] },
|
|
151
|
+
// { key: 'b', values: [{ str: 'b', total: 400 }] }
|
|
152
|
+
// ]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
<!-- keywords: export, object, dictionary, lookup, key-value -->
|
|
158
|
+
## groupBy.object()
|
|
159
|
+
|
|
160
|
+
Returns `{ key: values, ... }` -- a plain object keyed by group values.
|
|
161
|
+
|
|
162
|
+
**Signature:** `groupBy.object(options?: ExportOptions)`
|
|
163
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
164
|
+
|
|
165
|
+
### Example
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
tidy(
|
|
169
|
+
data,
|
|
170
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.object())
|
|
171
|
+
)
|
|
172
|
+
// output:
|
|
173
|
+
// {
|
|
174
|
+
// a: [{ str: 'a', total: 3 }],
|
|
175
|
+
// b: [{ str: 'b', total: 400 }]
|
|
176
|
+
// }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Multi-key produces nested objects:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
tidy(
|
|
183
|
+
data,
|
|
184
|
+
groupBy(
|
|
185
|
+
['str', 'ing'],
|
|
186
|
+
[summarize({ total: sum('value') })],
|
|
187
|
+
groupBy.object({ single: true })
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
// output:
|
|
191
|
+
// {
|
|
192
|
+
// a: {
|
|
193
|
+
// x: { str: 'a', ing: 'x', total: 1 },
|
|
194
|
+
// y: { str: 'a', ing: 'y', total: 2 }
|
|
195
|
+
// },
|
|
196
|
+
// b: {
|
|
197
|
+
// x: { str: 'b', ing: 'x', total: 100 },
|
|
198
|
+
// y: { str: 'b', ing: 'y', total: 300 }
|
|
199
|
+
// }
|
|
200
|
+
// }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
<!-- keywords: export, map, ES Map -->
|
|
206
|
+
## groupBy.map()
|
|
207
|
+
|
|
208
|
+
Returns an ES `Map` where keys are group values (not tuples) and values are nested Maps or leaf arrays.
|
|
209
|
+
|
|
210
|
+
**Signature:** `groupBy.map(options?: ExportOptions)`
|
|
211
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
212
|
+
|
|
213
|
+
### Example
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
tidy(
|
|
217
|
+
data,
|
|
218
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.map())
|
|
219
|
+
)
|
|
220
|
+
// output:
|
|
221
|
+
// new Map([
|
|
222
|
+
// ['a', [{ str: 'a', total: 3 }]],
|
|
223
|
+
// ['b', [{ str: 'b', total: 400 }]]
|
|
224
|
+
// ])
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
<!-- keywords: export, grouped, internal, Grouped Map, tuple keys -->
|
|
230
|
+
## groupBy.grouped()
|
|
231
|
+
|
|
232
|
+
Returns the raw internal `Grouped<T>` structure -- a `Map` where keys are `[keyName, keyValue]` tuples.
|
|
233
|
+
|
|
234
|
+
**Signature:** `groupBy.grouped(options?: ExportOptions)`
|
|
235
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
236
|
+
|
|
237
|
+
### Example
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
tidy(
|
|
241
|
+
data,
|
|
242
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.grouped())
|
|
243
|
+
)
|
|
244
|
+
// output:
|
|
245
|
+
// new Map([
|
|
246
|
+
// [['str', 'a'], [{ str: 'a', total: 3 }]],
|
|
247
|
+
// [['str', 'b'], [{ str: 'b', total: 400 }]]
|
|
248
|
+
// ])
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
<!-- keywords: export, keys, group keys only -->
|
|
254
|
+
## groupBy.keys()
|
|
255
|
+
|
|
256
|
+
Returns just the group key values, no data.
|
|
257
|
+
|
|
258
|
+
**Signature:** `groupBy.keys(options?: ExportOptions)`
|
|
259
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
260
|
+
|
|
261
|
+
### Example
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
tidy(
|
|
265
|
+
data,
|
|
266
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.keys())
|
|
267
|
+
)
|
|
268
|
+
// output:
|
|
269
|
+
// ['a', 'b']
|
|
270
|
+
|
|
271
|
+
// Multi-key:
|
|
272
|
+
tidy(
|
|
273
|
+
data,
|
|
274
|
+
groupBy(['str', 'ing'], [summarize({ total: sum('value') })], groupBy.keys())
|
|
275
|
+
)
|
|
276
|
+
// output:
|
|
277
|
+
// [['a', ['x', 'y']], ['b', ['x', 'y']]]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
<!-- keywords: export, values, grouped values only -->
|
|
283
|
+
## groupBy.values()
|
|
284
|
+
|
|
285
|
+
Returns just the grouped value arrays, no keys.
|
|
286
|
+
|
|
287
|
+
**Signature:** `groupBy.values(options?: ExportOptions)`
|
|
288
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
289
|
+
|
|
290
|
+
### Example
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
tidy(
|
|
294
|
+
data,
|
|
295
|
+
groupBy('str', [summarize({ total: sum('value') })], groupBy.values())
|
|
296
|
+
)
|
|
297
|
+
// output:
|
|
298
|
+
// [
|
|
299
|
+
// [{ str: 'a', total: 3 }],
|
|
300
|
+
// [{ str: 'b', total: 400 }]
|
|
301
|
+
// ]
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
<!-- keywords: export, levels, per-level, mixed export, custom levels -->
|
|
307
|
+
## groupBy.levels()
|
|
308
|
+
|
|
309
|
+
Per-level export control for multi-key grouping. Each level can use a different export format. The last level specified repeats for remaining levels.
|
|
310
|
+
|
|
311
|
+
**Signature:** `groupBy.levels(options: ExportOptions & { levels: LevelSpec[] })`
|
|
312
|
+
**Goes inside:** 3rd argument of `groupBy()`
|
|
313
|
+
|
|
314
|
+
### Parameters (in the options object)
|
|
315
|
+
|
|
316
|
+
- **levels** (required) -- array of export type strings, one per grouping level:
|
|
317
|
+
`'entries'` | `'entries-object'` | `'object'` | `'map'` | `'keys'` | `'values'` | `LevelSpec`
|
|
318
|
+
- Plus all common export options (`single`, `flat`, `mapLeaf`, etc.)
|
|
319
|
+
|
|
320
|
+
### Example
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
tidy(
|
|
324
|
+
data,
|
|
325
|
+
groupBy(
|
|
326
|
+
['str', 'ing'],
|
|
327
|
+
[summarize({ total: sum('value') })],
|
|
328
|
+
groupBy.levels({ levels: ['entries-object', 'object'], single: true })
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
// output:
|
|
332
|
+
// [
|
|
333
|
+
// {
|
|
334
|
+
// key: 'a',
|
|
335
|
+
// values: {
|
|
336
|
+
// x: { str: 'a', ing: 'x', total: 1 },
|
|
337
|
+
// y: { str: 'a', ing: 'y', total: 2 }
|
|
338
|
+
// }
|
|
339
|
+
// },
|
|
340
|
+
// {
|
|
341
|
+
// key: 'b',
|
|
342
|
+
// values: {
|
|
343
|
+
// x: { str: 'b', ing: 'x', total: 100 },
|
|
344
|
+
// y: { str: 'b', ing: 'y', total: 300 }
|
|
345
|
+
// }
|
|
346
|
+
// }
|
|
347
|
+
// ]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
<!-- keywords: export options, single, flat, compositeKey, mapLeaf, mapLeaves, mapEntry, addGroupKeys -->
|
|
353
|
+
## Common Export Options
|
|
354
|
+
|
|
355
|
+
All export shortcut helpers (`groupBy.entries()`, `groupBy.object()`, etc.) accept an options object with these fields:
|
|
356
|
+
|
|
357
|
+
| Option | Type | Default | Description |
|
|
358
|
+
|---|---|---|---|
|
|
359
|
+
| `single` | `boolean` | `false` | Unwrap single-item leaf arrays to just the item. Typical after `summarize`. |
|
|
360
|
+
| `flat` | `boolean` | `false` | Flatten nested groups to a single level (multi-key grouping becomes one level). |
|
|
361
|
+
| `compositeKey` | `(keys: any[]) => string` | `keys.join('/')` | Custom function to create flat keys. Only used when `flat: true`. |
|
|
362
|
+
| `mapLeaf` | `(value: T) => any` | identity | Transform each individual leaf item during export. |
|
|
363
|
+
| `mapLeaves` | `(values: T[]) => any` | identity | Transform each leaf array (similar to d3 `rollup`). |
|
|
364
|
+
| `mapEntry` | `(entry: [key, values], level) => any` | identity | Transform each entry (for `entries` export only). |
|
|
365
|
+
| `addGroupKeys` | `boolean` | `true` | Set to `false` to prevent merging group keys back into result objects. |
|
|
366
|
+
|
|
367
|
+
### Example: flat with compositeKey
|
|
368
|
+
|
|
369
|
+
```js
|
|
370
|
+
tidy(
|
|
371
|
+
data,
|
|
372
|
+
groupBy(
|
|
373
|
+
['str', 'ing'],
|
|
374
|
+
[summarize({ total: sum('value') })],
|
|
375
|
+
groupBy.object({ flat: true, single: true, compositeKey: (keys) => keys.join('-') })
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
// output:
|
|
379
|
+
// {
|
|
380
|
+
// 'a-x': { str: 'a', ing: 'x', total: 1 },
|
|
381
|
+
// 'a-y': { str: 'a', ing: 'y', total: 2 },
|
|
382
|
+
// 'b-x': { str: 'b', ing: 'x', total: 100 },
|
|
383
|
+
// 'b-y': { str: 'b', ing: 'y', total: 300 }
|
|
384
|
+
// }
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Example: mapLeaves
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
tidy(
|
|
391
|
+
data,
|
|
392
|
+
groupBy(
|
|
393
|
+
'str',
|
|
394
|
+
[summarize({ total: sum('value') })],
|
|
395
|
+
groupBy.entries({ mapLeaves: (leaves) => leaves.length })
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
// output:
|
|
399
|
+
// [['a', 1], ['b', 1]]
|
|
400
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Joins
|
|
2
|
+
|
|
3
|
+
Join two collections by matching keys.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { tidy, innerJoin, leftJoin, fullJoin } from '@tidyjs/tidy';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- keywords: inner join, merge, combine, matching rows -->
|
|
12
|
+
## innerJoin
|
|
13
|
+
|
|
14
|
+
Keep only rows that have a match in both collections.
|
|
15
|
+
|
|
16
|
+
**Signature:** `innerJoin<T, JoinT>(itemsToJoin: JoinT[], options?: { by?: string | string[] | Record<string, string> })`
|
|
17
|
+
**Goes inside:** `tidy(data, innerJoin(...))`
|
|
18
|
+
|
|
19
|
+
### Parameters
|
|
20
|
+
- **itemsToJoin** `JoinT[]` -- the right-side array to join against.
|
|
21
|
+
- **options.by** `string | string[] | { [rightKey]: leftKey }` -- how to match rows. A single key name, array of shared key names, or an object mapping right-side keys to left-side keys. If omitted, auto-detects shared keys from the first elements of each array.
|
|
22
|
+
|
|
23
|
+
### Example
|
|
24
|
+
```js
|
|
25
|
+
const data = [
|
|
26
|
+
{ id: 1, value: 10 },
|
|
27
|
+
{ id: 2, value: 20 },
|
|
28
|
+
];
|
|
29
|
+
const lookup = [
|
|
30
|
+
{ id: 1, label: 'a' },
|
|
31
|
+
{ id: 3, label: 'c' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
tidy(data, innerJoin(lookup, { by: 'id' }));
|
|
35
|
+
// output: [{ id: 1, value: 10, label: 'a' }]
|
|
36
|
+
// row id=2 dropped (no match), row id=3 dropped (not in left)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<!-- keywords: left join, merge, keep all left, nullable right -->
|
|
42
|
+
## leftJoin
|
|
43
|
+
|
|
44
|
+
Keep all left-side rows. Adds right-side columns where a match exists; fills with `undefined` where no match is found.
|
|
45
|
+
|
|
46
|
+
**Signature:** `leftJoin<T, JoinT>(itemsToJoin: JoinT[], options?: { by?: string | string[] | Record<string, string> })`
|
|
47
|
+
**Goes inside:** `tidy(data, leftJoin(...))`
|
|
48
|
+
|
|
49
|
+
### Parameters
|
|
50
|
+
- **itemsToJoin** `JoinT[]` -- the right-side array to join against.
|
|
51
|
+
- **options.by** -- same as `innerJoin`. If omitted, auto-detects shared keys.
|
|
52
|
+
|
|
53
|
+
### Example
|
|
54
|
+
```js
|
|
55
|
+
const data = [
|
|
56
|
+
{ id: 1, value: 10 },
|
|
57
|
+
{ id: 2, value: 20 },
|
|
58
|
+
];
|
|
59
|
+
const lookup = [
|
|
60
|
+
{ id: 1, label: 'a' },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
tidy(data, leftJoin(lookup, { by: 'id' }));
|
|
64
|
+
// output:
|
|
65
|
+
// [
|
|
66
|
+
// { id: 1, value: 10, label: 'a' },
|
|
67
|
+
// { id: 2, value: 20, label: undefined },
|
|
68
|
+
// ]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
<!-- keywords: full join, outer join, merge, keep all rows -->
|
|
74
|
+
## fullJoin
|
|
75
|
+
|
|
76
|
+
Keep all rows from both sides. Unmatched columns are filled with `undefined`.
|
|
77
|
+
|
|
78
|
+
**Signature:** `fullJoin<T, JoinT>(itemsToJoin: JoinT[], options?: { by?: string | string[] | Record<string, string> })`
|
|
79
|
+
**Goes inside:** `tidy(data, fullJoin(...))`
|
|
80
|
+
|
|
81
|
+
### Parameters
|
|
82
|
+
- **itemsToJoin** `JoinT[]` -- the right-side array to join against.
|
|
83
|
+
- **options.by** -- same as `innerJoin`. If omitted, auto-detects shared keys.
|
|
84
|
+
|
|
85
|
+
### Example
|
|
86
|
+
```js
|
|
87
|
+
const data = [
|
|
88
|
+
{ id: 1, value: 10 },
|
|
89
|
+
{ id: 2, value: 20 },
|
|
90
|
+
];
|
|
91
|
+
const other = [
|
|
92
|
+
{ id: 2, label: 'b' },
|
|
93
|
+
{ id: 3, label: 'c' },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
tidy(data, fullJoin(other, { by: 'id' }));
|
|
97
|
+
// output:
|
|
98
|
+
// [
|
|
99
|
+
// { id: 1, value: 10, label: undefined },
|
|
100
|
+
// { id: 2, value: 20, label: 'b' },
|
|
101
|
+
// { id: undefined, value: undefined, label: 'c' },
|
|
102
|
+
// ]
|
|
103
|
+
// id=3 row added from right side with left columns undefined
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Joining on different key names
|
|
109
|
+
|
|
110
|
+
Use an object `{ rightKey: 'leftKey' }` to join when column names differ:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const orders = [{ orderId: 1, userId: 100 }];
|
|
114
|
+
const users = [{ uid: 100, name: 'Alice' }];
|
|
115
|
+
|
|
116
|
+
tidy(orders, leftJoin(users, { by: { uid: 'userId' } }));
|
|
117
|
+
// output: [{ orderId: 1, userId: 100, uid: 100, name: 'Alice' }]
|
|
118
|
+
```
|