@orieljs/framework 0.1.0-alpha.6 → 0.1.0-alpha.7
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/NOTICE +4 -0
- package/README.md +10 -1
- package/TRADEMARKS.md +33 -0
- package/dist/{chunk-ZOEPKBVF.js → chunk-4MWG3B7G.js} +1347 -428
- package/dist/chunk-4MWG3B7G.js.map +1 -0
- package/dist/{chunk-POEP4B2M.js → chunk-7ZQZYDOC.js} +745 -105
- package/dist/chunk-7ZQZYDOC.js.map +1 -0
- package/dist/{chunk-I63ZFMX6.js → chunk-GVNLZZTN.js} +1 -1
- package/dist/chunk-GVNLZZTN.js.map +1 -0
- package/dist/{chunk-MFTZQMZM.js → chunk-VOYW3R5J.js} +4 -4
- package/dist/chunk-VOYW3R5J.js.map +1 -0
- package/dist/contracts.d.ts +515 -17
- package/dist/contracts.js +1 -1
- package/dist/index.d-WQpfdIbG.d.ts +630 -0
- package/dist/index.d.ts +964 -152
- package/dist/index.js +4279 -866
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-CBZQYYDS.d.ts → jsx-runtime-6um4Mwfb.d.ts} +96 -5
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +3 -3
- package/dist/{testing-BM481Hdz.d.ts → testing-CKwfWJGn.d.ts} +180 -15
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +3 -3
- package/docs/authentication.md +487 -0
- package/docs/authorization.md +435 -0
- package/docs/cache.md +647 -0
- package/docs/collections.md +2270 -0
- package/docs/configuration.md +334 -0
- package/docs/console.md +651 -0
- package/docs/container.md +430 -0
- package/docs/controllers.md +406 -0
- package/docs/csrf.md +126 -0
- package/docs/database-testing.md +144 -0
- package/docs/database.md +236 -0
- package/docs/deployment.md +276 -0
- package/docs/eloquent-collections.md +218 -0
- package/docs/eloquent-factories.md +255 -0
- package/docs/eloquent-mutators.md +254 -0
- package/docs/eloquent-observers.md +145 -0
- package/docs/eloquent-relationships.md +557 -0
- package/docs/eloquent.md +494 -0
- package/docs/encryption.md +104 -0
- package/docs/errors.md +318 -0
- package/docs/events.md +336 -0
- package/docs/facades.md +278 -0
- package/docs/hashing.md +118 -0
- package/docs/helpers.md +1281 -0
- package/docs/http-tests.md +386 -0
- package/docs/inertia.md +485 -0
- package/docs/installation.md +209 -0
- package/docs/lifecycle.md +234 -0
- package/docs/live-tests.md +226 -0
- package/docs/live.md +910 -0
- package/docs/logging.md +255 -0
- package/docs/mail.md +619 -0
- package/docs/middleware.md +384 -0
- package/docs/migrations.md +416 -0
- package/docs/mocking.md +264 -0
- package/docs/navigation.json +273 -0
- package/docs/notifications.md +225 -0
- package/docs/pagination.md +247 -0
- package/docs/passwords.md +207 -0
- package/docs/providers.md +187 -0
- package/docs/queries.md +600 -0
- package/docs/queues.md +330 -0
- package/docs/requests.md +533 -0
- package/docs/responses.md +444 -0
- package/docs/routing.md +738 -0
- package/docs/scheduling.md +277 -0
- package/docs/seeding.md +122 -0
- package/docs/session.md +409 -0
- package/docs/starter-kits.md +396 -0
- package/docs/storage.md +192 -0
- package/docs/structure.md +264 -0
- package/docs/testing.md +165 -0
- package/docs/time-travel.md +138 -0
- package/docs/validation.md +1792 -0
- package/docs/verification.md +152 -0
- package/docs/views.md +566 -0
- package/docs/vite.md +322 -0
- package/package.json +34 -32
- package/dist/chunk-I63ZFMX6.js.map +0 -1
- package/dist/chunk-MFTZQMZM.js.map +0 -1
- package/dist/chunk-POEP4B2M.js.map +0 -1
- package/dist/chunk-ZOEPKBVF.js.map +0 -1
- package/dist/index.d-BJyJx6U1.d.ts +0 -319
|
@@ -0,0 +1,2270 @@
|
|
|
1
|
+
# Collections
|
|
2
|
+
|
|
3
|
+
A collection wraps a list of items with Laravel's collection methods: filter it, map it, group it, sort it and sum
|
|
4
|
+
it, one chained call after another.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
`Collection` wraps an array. Here `collect()` makes a collection of the array, uppercases each name, and drops the
|
|
9
|
+
empty one:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const collection = collect(['Taylor', 'Abigail', null])
|
|
13
|
+
.map((name) => name?.toUpperCase())
|
|
14
|
+
.reject((name) => !name)
|
|
15
|
+
|
|
16
|
+
collection.all()
|
|
17
|
+
// ['TAYLOR', 'ABIGAIL']
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Methods that change the items, such as `map()` and `reject()`, return a new collection and leave the one you called
|
|
21
|
+
them on as it was. A few methods change the collection itself, and say so: `push()`, `pop()`, `put()`, `transform()`
|
|
22
|
+
and the others listed under [Changing a collection in place](#changing-a-collection-in-place).
|
|
23
|
+
|
|
24
|
+
Collections are iterable, so `for...of` and the spread operator work on them, and `JSON.stringify()` writes their items
|
|
25
|
+
as an array. Your queries return collections too: a query's models come back in an `EloquentCollection`, which has every
|
|
26
|
+
method here and a few for models, covered on [Eloquent collections](eloquent-collections.md).
|
|
27
|
+
|
|
28
|
+
### Creating collections
|
|
29
|
+
|
|
30
|
+
`collect()` makes a collection from an array, or from anything iterable, such as a `Set`:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const collection = collect([1, 2, 3])
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`Collection.make()` does the same, and [`times()`](#times), [`range()`](#range), [`wrap()`](#wrap) and
|
|
37
|
+
[`fromJson()`](#fromjson) build one in other ways. A collection's type comes from its items: `collect([1, 2, 3])` is
|
|
38
|
+
a `Collection<number>`, and the callbacks you give its methods are typed with it.
|
|
39
|
+
|
|
40
|
+
### A collection is a list
|
|
41
|
+
|
|
42
|
+
A Laravel collection is a PHP array, whose items have keys: numbers, as in a list, or strings, as in a dictionary.
|
|
43
|
+
An Oriel collection is a list. Its items are always numbered from 0, in order, with no gaps:
|
|
44
|
+
|
|
45
|
+
- methods that keep some of the items, such as `filter()`, `unique()` and `sortBy()`, number them again from 0. You
|
|
46
|
+
never need `values()` to reset the keys, as you do in Laravel;
|
|
47
|
+
- Laravel's methods that take a key take an index: `get(1)`, `has(0)`, `only(0, 2)`, `except(1)`, `put(1, value)`,
|
|
48
|
+
`pull(1)`, `forget(1)`, `replace({ 1: value })` and `splice()`. `search()` returns an index, and `keys()` the
|
|
49
|
+
indexes;
|
|
50
|
+
- removing an item, with `forget()` or `pull()`, moves the later items up;
|
|
51
|
+
- `put()` and `replace()` with an index past the end add the item at the end. A negative or fractional index throws a
|
|
52
|
+
`RangeError`: "Invalid collection index [-1].";
|
|
53
|
+
- methods that build a dictionary return a plain object, not a collection: [`keyBy()`](#keyby),
|
|
54
|
+
[`groupBy()`](#groupby), [`mapWithKeys()`](#mapwithkeys), [`countBy()`](#countby), [`flip()`](#flip),
|
|
55
|
+
[`combine()`](#combine), [`mapToDictionary()`](#maptodictionary) and [`mapToGroups()`](#maptogroups).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const collection = collect(['a', 'b', 'c'])
|
|
59
|
+
|
|
60
|
+
collection.forget(0)
|
|
61
|
+
|
|
62
|
+
collection.all()
|
|
63
|
+
// ['b', 'c']
|
|
64
|
+
|
|
65
|
+
collection.get(0)
|
|
66
|
+
// 'b'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Laravel's methods that only make sense for keys aren't in Oriel; [Methods left out](#methods-left-out) lists them.
|
|
70
|
+
|
|
71
|
+
### Strict and loose comparisons
|
|
72
|
+
|
|
73
|
+
Laravel compares values loosely in most methods, so the string `'1'` equals the number `1`, and offers a strict twin
|
|
74
|
+
for some of them. Oriel compares loosely, with JavaScript's `==`, only in these:
|
|
75
|
+
|
|
76
|
+
- `where(key, value)`, `firstWhere(key, value)`, `every(key, value)`, `sole(key, value)`, `firstOrFail(key, value)`,
|
|
77
|
+
and every form that takes an operator, such as `where('price', '>', 100)` and `contains('price', '=', 100)`;
|
|
78
|
+
- `search()`, `before()` and `after()`, unless you pass `true` as their last argument;
|
|
79
|
+
- `duplicates()`, whose strict twin is `duplicatesStrict()`.
|
|
80
|
+
|
|
81
|
+
Everything else compares strictly, with `===`: `contains(value)`, `contains(key, value)`, `some()` and
|
|
82
|
+
`doesntContain()` with a value, `unique()`, `whereIn()`, `whereNotIn()`, `diff()`, `intersect()`, `takeUntil()` and
|
|
83
|
+
the others. Their `Strict` names, such as `containsStrict()` and `uniqueStrict()`, are the same methods under
|
|
84
|
+
Laravel's other name.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const products = collect([{ product: 'Desk', price: 200 }])
|
|
88
|
+
|
|
89
|
+
products.where('price', '200').count()
|
|
90
|
+
// 1
|
|
91
|
+
|
|
92
|
+
products.contains('price', '200')
|
|
93
|
+
// false
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> [!NOTE]
|
|
97
|
+
> In Laravel, `contains('price', '200')` is true and `where('price', '200')` finds the desk, both loosely. In Oriel,
|
|
98
|
+
> `contains()`, `some()` and `doesntContain()` with a key and a value are strict, while `where()`, `every()`,
|
|
99
|
+
> `sole()` and `firstOrFail()` with a key and a value are loose. Pass the value with the type you stored, and the
|
|
100
|
+
> difference never shows.
|
|
101
|
+
|
|
102
|
+
### Keys and callbacks
|
|
103
|
+
|
|
104
|
+
Many methods read a value from each item: give them the name of a property, as `sortBy('price')`, or a function
|
|
105
|
+
that returns the value, as `sortBy((product) => product.price)`. TypeScript checks a name against the items' type. A
|
|
106
|
+
callback receives the item and its index.
|
|
107
|
+
|
|
108
|
+
Where Laravel's callback returns `null` to mean "no value", Oriel's methods return `undefined`: `first()` on an
|
|
109
|
+
empty collection, `avg()` with no numbers, `get()` past the end.
|
|
110
|
+
|
|
111
|
+
### Extending collections
|
|
112
|
+
|
|
113
|
+
Laravel adds methods to every collection with `Collection::macro()`. Oriel has no macros: methods added while your
|
|
114
|
+
app runs are invisible to TypeScript. Extend `Collection` instead:
|
|
115
|
+
|
|
116
|
+
```ts title="app/Support/ProductCollection.ts"
|
|
117
|
+
import { Collection } from '@orieljs/framework'
|
|
118
|
+
|
|
119
|
+
interface Product {
|
|
120
|
+
name: string
|
|
121
|
+
price: number
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export class ProductCollection extends Collection<Product> {
|
|
125
|
+
total(): number {
|
|
126
|
+
return this.sum('price')
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Methods that keep the same kind of items, such as `filter()`, `sortBy()`, `where()` and `take()`, return your
|
|
132
|
+
class, so `new ProductCollection(products).where('price', '>', 100).total()` works. Methods that make other items,
|
|
133
|
+
such as `map()` and `pluck()`, return a plain `Collection`. A subclass whose constructor takes other arguments must
|
|
134
|
+
override the protected `newInstance(items)`, which the methods use to make their result.
|
|
135
|
+
|
|
136
|
+
## Available methods
|
|
137
|
+
|
|
138
|
+
Each method below has a short entry, in alphabetical order. Static methods, called on `Collection`, are marked so.
|
|
139
|
+
|
|
140
|
+
[add](#add), [after](#after), [all](#all), [average](#average), [avg](#avg), [before](#before), [chunk](#chunk),
|
|
141
|
+
[chunkWhile](#chunkwhile), [collapse](#collapse), [collect](#collect), [combine](#combine), [concat](#concat),
|
|
142
|
+
[contains](#contains), [containsOneItem](#containsoneitem), [containsStrict](#containsstrict), [count](#count),
|
|
143
|
+
[countBy](#countby), [crossJoin](#crossjoin), [diff](#diff), [diffUsing](#diffusing), [doesntContain](#doesntcontain),
|
|
144
|
+
[doesntContainStrict](#doesntcontainstrict), [dump](#dump), [duplicates](#duplicates),
|
|
145
|
+
[duplicatesStrict](#duplicatesstrict), [each](#each), [eachSpread](#eachspread), [empty](#empty), [ensure](#ensure),
|
|
146
|
+
[every](#every), [except](#except), [filter](#filter), [first](#first), [firstOrFail](#firstorfail),
|
|
147
|
+
[firstWhere](#firstwhere), [flatMap](#flatmap), [flatten](#flatten), [flip](#flip), [forget](#forget),
|
|
148
|
+
[forPage](#forpage), [fromJson](#fromjson), [get](#get), [groupBy](#groupby), [has](#has), [hasAny](#hasany),
|
|
149
|
+
[implode](#implode), [intersect](#intersect), [intersectUsing](#intersectusing), [isEmpty](#isempty),
|
|
150
|
+
[isNotEmpty](#isnotempty), [join](#join), [keyBy](#keyby), [keys](#keys), [last](#last), [lazy](#lazy), [make](#make),
|
|
151
|
+
[map](#map), [mapInto](#mapinto), [mapSpread](#mapspread), [mapToDictionary](#maptodictionary),
|
|
152
|
+
[mapToGroups](#maptogroups), [mapWithKeys](#mapwithkeys), [max](#max), [median](#median), [merge](#merge), [min](#min),
|
|
153
|
+
[mode](#mode), [multiply](#multiply), [nth](#nth), [only](#only), [pad](#pad), [partition](#partition),
|
|
154
|
+
[percentage](#percentage), [pipe](#pipe), [pipeInto](#pipeinto), [pipeThrough](#pipethrough), [pluck](#pluck),
|
|
155
|
+
[pop](#pop), [prepend](#prepend), [pull](#pull), [push](#push), [put](#put), [random](#random), [range](#range),
|
|
156
|
+
[reduce](#reduce), [reduceSpread](#reducespread), [reject](#reject), [replace](#replace), [reverse](#reverse),
|
|
157
|
+
[search](#search), [select](#select), [shift](#shift), [shuffle](#shuffle), [skip](#skip), [skipUntil](#skipuntil),
|
|
158
|
+
[skipWhile](#skipwhile), [slice](#slice), [sliding](#sliding), [sole](#sole), [some](#some), [sort](#sort),
|
|
159
|
+
[sortBy](#sortby), [sortByDesc](#sortbydesc), [sortDesc](#sortdesc), [splice](#splice), [split](#split),
|
|
160
|
+
[splitIn](#splitin), [sum](#sum), [take](#take), [takeUntil](#takeuntil), [takeWhile](#takewhile), [tap](#tap),
|
|
161
|
+
[times](#times), [toArray](#toarray), [toJson](#tojson), [transform](#transform), [unique](#unique),
|
|
162
|
+
[uniqueStrict](#uniquestrict), [unless](#unless), [unlessEmpty](#unlessempty), [unlessNotEmpty](#unlessnotempty),
|
|
163
|
+
[unshift](#unshift), [unwrap](#unwrap), [value](#value), [values](#values), [when](#when), [whenEmpty](#whenempty),
|
|
164
|
+
[whenNotEmpty](#whennotempty), [where](#where), [whereBetween](#wherebetween), [whereIn](#wherein),
|
|
165
|
+
[whereInstanceOf](#whereinstanceof), [whereInStrict](#whereinstrict), [whereNotBetween](#wherenotbetween),
|
|
166
|
+
[whereNotIn](#wherenotin), [whereNotInStrict](#wherenotinstrict), [whereNotNull](#wherenotnull),
|
|
167
|
+
[whereNull](#wherenull), [whereStrict](#wherestrict), [wrap](#wrap), [zip](#zip)
|
|
168
|
+
|
|
169
|
+
### Changing a collection in place
|
|
170
|
+
|
|
171
|
+
These methods change the collection you call them on: [`add()`](#add), [`forget()`](#forget), [`pop()`](#pop),
|
|
172
|
+
[`prepend()`](#prepend), [`pull()`](#pull), [`push()`](#push), [`put()`](#put), [`shift()`](#shift),
|
|
173
|
+
[`splice()`](#splice), [`transform()`](#transform) and [`unshift()`](#unshift). Every other method leaves it as it
|
|
174
|
+
is.
|
|
175
|
+
|
|
176
|
+
### Methods left out
|
|
177
|
+
|
|
178
|
+
Laravel's methods for a collection's keys aren't in Oriel, because a collection is a list, whose keys are its
|
|
179
|
+
indexes: `diffAssoc()`, `diffAssocUsing()`, `diffKeys()`, `intersectAssoc()`, `intersectAssocUsing()`,
|
|
180
|
+
`intersectByKeys()`, `union()`, `mergeRecursive()`, `replaceRecursive()`, `sortKeys()`, `sortKeysDesc()`,
|
|
181
|
+
`sortKeysUsing()`, `collapseWithKeys()`, `dot()` and `undot()`. For keyed data, use an object or a `Map`, or build
|
|
182
|
+
one with [`keyBy()`](#keyby) or [`mapWithKeys()`](#mapwithkeys).
|
|
183
|
+
|
|
184
|
+
These aren't in Oriel either:
|
|
185
|
+
|
|
186
|
+
- `macro()`: see [Extending collections](#extending-collections);
|
|
187
|
+
- `dd()`: JavaScript can't stop the program where you call it. [`dump()`](#dump) logs the items;
|
|
188
|
+
- `hasMany()`: compare [`count()`](#count) with 1;
|
|
189
|
+
- `hasSole()`: Oriel names it [`containsOneItem()`](#containsoneitem);
|
|
190
|
+
- higher order messages, such as `$users->each->markAsVip()`: pass a function,
|
|
191
|
+
`users.each((user) => user.markAsVip())`;
|
|
192
|
+
- dot notation in keys, such as `pluck('user.name')`: give the method a function where it takes one, or `map()` the
|
|
193
|
+
value out first: `map((item) => item.user.name)`.
|
|
194
|
+
|
|
195
|
+
## Method listing
|
|
196
|
+
|
|
197
|
+
#### `add()`
|
|
198
|
+
|
|
199
|
+
`add()` adds one item at the end of the collection, changing it, and returns the collection:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
const collection = collect([1, 2])
|
|
203
|
+
|
|
204
|
+
collection.add(3)
|
|
205
|
+
|
|
206
|
+
collection.all()
|
|
207
|
+
// [1, 2, 3]
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### `after()`
|
|
211
|
+
|
|
212
|
+
`after()` returns the item after the given one, or `undefined` when the item is the last one or isn't there:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
216
|
+
|
|
217
|
+
collection.after(3)
|
|
218
|
+
// 4
|
|
219
|
+
|
|
220
|
+
collection.after(5)
|
|
221
|
+
// undefined
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
It compares loosely, so `'4'` finds `4`. Pass `true` to compare strictly:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
collect<number | string>([2, 4, 6, 8]).after('4', true)
|
|
228
|
+
// undefined
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
It also takes a function, and returns the item after the first one that passes it:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
collect([2, 4, 6, 8]).after((item) => item > 5)
|
|
235
|
+
// 8
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### `all()`
|
|
239
|
+
|
|
240
|
+
`all()` returns the items, as a new array:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
collect([1, 2, 3]).all()
|
|
244
|
+
// [1, 2, 3]
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### `average()`
|
|
248
|
+
|
|
249
|
+
Another name for [`avg()`](#avg).
|
|
250
|
+
|
|
251
|
+
#### `avg()`
|
|
252
|
+
|
|
253
|
+
`avg()` returns the average of the items, or of a key's values:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
collect([{ foo: 10 }, { foo: 10 }, { foo: 20 }, { foo: 40 }]).avg('foo')
|
|
257
|
+
// 20
|
|
258
|
+
|
|
259
|
+
collect([1, 1, 2, 4]).avg()
|
|
260
|
+
// 2
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Only numbers count: a string such as `'1'`, `null` and other values are left out, and a collection with no numbers
|
|
264
|
+
gives `undefined`. Laravel counts numeric strings, and gives `null`.
|
|
265
|
+
|
|
266
|
+
#### `before()`
|
|
267
|
+
|
|
268
|
+
`before()` is the opposite of [`after()`](#after): it returns the item before the given one, or `undefined`:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
272
|
+
|
|
273
|
+
collection.before(3)
|
|
274
|
+
// 2
|
|
275
|
+
|
|
276
|
+
collection.before(1)
|
|
277
|
+
// undefined
|
|
278
|
+
|
|
279
|
+
collect([2, 4, 6, 8]).before((item) => item > 5)
|
|
280
|
+
// 4
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### `chunk()`
|
|
284
|
+
|
|
285
|
+
`chunk()` splits the collection into collections of the given size, the last one smaller when the items run out:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
const chunks = collect([1, 2, 3, 4, 5, 6, 7]).chunk(4)
|
|
289
|
+
|
|
290
|
+
chunks.toArray()
|
|
291
|
+
// [[1, 2, 3, 4], [5, 6, 7]]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### `chunkWhile()`
|
|
295
|
+
|
|
296
|
+
`chunkWhile()` splits the collection where the callback returns false. The callback receives the item, its index and
|
|
297
|
+
the chunk so far:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const chunks = collect('AABBCCCD'.split('')).chunkWhile((value, index, chunk) => value === chunk.last())
|
|
301
|
+
|
|
302
|
+
chunks.toArray()
|
|
303
|
+
// [['A', 'A'], ['B', 'B'], ['C', 'C', 'C'], ['D']]
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
#### `collapse()`
|
|
307
|
+
|
|
308
|
+
`collapse()` merges a collection of arrays, or of collections, into one flat collection. Other items are dropped:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
const collapsed = collect([
|
|
312
|
+
[1, 2, 3],
|
|
313
|
+
[4, 5, 6],
|
|
314
|
+
[7, 8, 9],
|
|
315
|
+
]).collapse()
|
|
316
|
+
|
|
317
|
+
collapsed.all()
|
|
318
|
+
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### `collect()`
|
|
322
|
+
|
|
323
|
+
`collect()` returns a plain `Collection` with the same items. On a subclass, such as the `EloquentCollection` your
|
|
324
|
+
queries return, it gives you a base collection:
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
const collection = collect([1, 2, 3]).collect()
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### `combine()`
|
|
331
|
+
|
|
332
|
+
`combine()` uses the items as keys for the values you give, and returns a plain object. Both must have the same
|
|
333
|
+
number of items, or it throws a `RangeError`:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
collect(['name', 'age']).combine(['George', 29])
|
|
337
|
+
// { name: 'George', age: 29 }
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### `concat()`
|
|
341
|
+
|
|
342
|
+
`concat()` returns the items followed by the ones you give:
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
const concatenated = collect(['John Doe']).concat(['Jane Doe']).concat(['Johnny Doe'])
|
|
346
|
+
|
|
347
|
+
concatenated.all()
|
|
348
|
+
// ['John Doe', 'Jane Doe', 'Johnny Doe']
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
#### `contains()`
|
|
352
|
+
|
|
353
|
+
`contains()` tells you whether the collection has an item. Given a function, it tells you whether an item passes it:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
collect([1, 2, 3, 4, 5]).contains((value) => value > 5)
|
|
357
|
+
// false
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Given a value, it tells you whether an item is that value:
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
const collection = collect(['Desk', 'Chair'])
|
|
364
|
+
|
|
365
|
+
collection.contains('Desk')
|
|
366
|
+
// true
|
|
367
|
+
|
|
368
|
+
collection.contains('New York')
|
|
369
|
+
// false
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Given a key and a value, it tells you whether an item has that value:
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
const collection = collect([
|
|
376
|
+
{ product: 'Desk', price: 200 },
|
|
377
|
+
{ product: 'Chair', price: 100 },
|
|
378
|
+
])
|
|
379
|
+
|
|
380
|
+
collection.contains('product', 'Bookcase')
|
|
381
|
+
// false
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
A value, and a key and a value, are compared strictly, where Laravel compares them loosely:
|
|
385
|
+
`collect([1, 2]).contains('1')` is `false`. Given a key, an operator and a value, it compares as [`where()`](#where)
|
|
386
|
+
does, loosely: `collection.contains('price', '>', 150)`.
|
|
387
|
+
|
|
388
|
+
#### `containsOneItem()`
|
|
389
|
+
|
|
390
|
+
`containsOneItem()` tells you whether the collection has exactly one item, or exactly one that passes the callback.
|
|
391
|
+
It is Laravel's `hasSole()`:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
collect([]).containsOneItem()
|
|
395
|
+
// false
|
|
396
|
+
|
|
397
|
+
collect(['1']).containsOneItem()
|
|
398
|
+
// true
|
|
399
|
+
|
|
400
|
+
collect([1, 2, 3]).containsOneItem((item) => item === 2)
|
|
401
|
+
// true
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### `containsStrict()`
|
|
405
|
+
|
|
406
|
+
The same as [`contains()`](#contains), which already compares strictly.
|
|
407
|
+
|
|
408
|
+
#### `count()`
|
|
409
|
+
|
|
410
|
+
`count()` returns the number of items:
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
collect([1, 2, 3, 4]).count()
|
|
414
|
+
// 4
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
#### `countBy()`
|
|
418
|
+
|
|
419
|
+
`countBy()` counts how many times each value appears, and returns a plain object:
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
collect([1, 2, 2, 2, 3]).countBy()
|
|
423
|
+
// { 1: 1, 2: 3, 3: 1 }
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Given a key or a function, it counts what that gives for each item:
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']).countBy((email) => email.split('@')[1])
|
|
430
|
+
// { 'gmail.com': 2, 'yahoo.com': 1 }
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
#### `crossJoin()`
|
|
434
|
+
|
|
435
|
+
`crossJoin()` returns every combination of the items with the items of the lists you give:
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
collect([1, 2]).crossJoin(['a', 'b']).all()
|
|
439
|
+
// [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
|
|
440
|
+
|
|
441
|
+
collect([1, 2]).crossJoin(['a', 'b'], ['I', 'II']).all()
|
|
442
|
+
// [[1, 'a', 'I'], [1, 'a', 'II'], [1, 'b', 'I'], [1, 'b', 'II'],
|
|
443
|
+
// [2, 'a', 'I'], [2, 'a', 'II'], [2, 'b', 'I'], [2, 'b', 'II']]
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### `diff()`
|
|
447
|
+
|
|
448
|
+
`diff()` returns the items that aren't in the list you give, compared strictly:
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
const diff = collect([1, 2, 3, 4, 5]).diff([2, 4, 6, 8])
|
|
452
|
+
|
|
453
|
+
diff.all()
|
|
454
|
+
// [1, 3, 5]
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Laravel compares the items as strings, so `1` and `'1'` match there and not here.
|
|
458
|
+
|
|
459
|
+
#### `diffUsing()`
|
|
460
|
+
|
|
461
|
+
`diffUsing()` is [`diff()`](#diff) with your own comparison: a function that returns `0` for two items that are the
|
|
462
|
+
same:
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
const diff = collect(['a', 'B', 'c']).diffUsing(['A', 'b'], (a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
466
|
+
|
|
467
|
+
diff.all()
|
|
468
|
+
// ['c']
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
#### `doesntContain()`
|
|
472
|
+
|
|
473
|
+
`doesntContain()` is the opposite of [`contains()`](#contains), and takes the same arguments, compared the same way:
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
collect([1, 2, 3, 4, 5]).doesntContain((value) => value < 5)
|
|
477
|
+
// false
|
|
478
|
+
|
|
479
|
+
collect(['Desk', 'Chair']).doesntContain('Table')
|
|
480
|
+
// true
|
|
481
|
+
|
|
482
|
+
collect([{ product: 'Desk', price: 200 }]).doesntContain('product', 'Bookcase')
|
|
483
|
+
// true
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
#### `doesntContainStrict()`
|
|
487
|
+
|
|
488
|
+
The same as [`doesntContain()`](#doesntcontain), which already compares strictly.
|
|
489
|
+
|
|
490
|
+
#### `dump()`
|
|
491
|
+
|
|
492
|
+
`dump()` logs the items with `console.log()`, and any values you pass after them, and returns the collection, so
|
|
493
|
+
you can call it in the middle of a chain:
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
collect(['John Doe', 'Jane Doe']).dump()
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### `duplicates()`
|
|
500
|
+
|
|
501
|
+
`duplicates()` returns the values that appear more than once, each time after the first, compared loosely:
|
|
502
|
+
|
|
503
|
+
```ts
|
|
504
|
+
collect(['a', 'b', 'a', 'c', 'b']).duplicates().all()
|
|
505
|
+
// ['a', 'b']
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Given a key or a function, it looks for repeated values of it:
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
const employees = collect([
|
|
512
|
+
{ email: 'abigail@example.com', position: 'Developer' },
|
|
513
|
+
{ email: 'james@example.com', position: 'Designer' },
|
|
514
|
+
{ email: 'victoria@example.com', position: 'Developer' },
|
|
515
|
+
])
|
|
516
|
+
|
|
517
|
+
employees.duplicates('position').all()
|
|
518
|
+
// ['Developer']
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Laravel's result keeps each duplicate's position as its key; Oriel's is a list of the values.
|
|
522
|
+
|
|
523
|
+
#### `duplicatesStrict()`
|
|
524
|
+
|
|
525
|
+
[`duplicates()`](#duplicates), compared strictly: `collect([1, '1']).duplicatesStrict()` is empty.
|
|
526
|
+
|
|
527
|
+
#### `each()`
|
|
528
|
+
|
|
529
|
+
`each()` calls the function with each item and its index. Return `false` to stop:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
collect([1, 2, 3, 4]).each((item) => {
|
|
533
|
+
if (item > 2) {
|
|
534
|
+
return false
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
console.log(item)
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### `eachSpread()`
|
|
542
|
+
|
|
543
|
+
`eachSpread()` is [`each()`](#each) for a collection of lists: each list's items are the function's arguments,
|
|
544
|
+
followed by the index:
|
|
545
|
+
|
|
546
|
+
```ts
|
|
547
|
+
collect([
|
|
548
|
+
['John Doe', 35],
|
|
549
|
+
['Jane Doe', 33],
|
|
550
|
+
]).eachSpread((name, age) => {
|
|
551
|
+
// ...
|
|
552
|
+
})
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
#### `empty()`
|
|
556
|
+
|
|
557
|
+
Static. `Collection.empty()` returns a collection with no items.
|
|
558
|
+
|
|
559
|
+
#### `ensure()`
|
|
560
|
+
|
|
561
|
+
`ensure()` checks that every item is of a type, or of one of a list of types, and throws a `TypeError` when one isn't:
|
|
562
|
+
"Collection should only include [number] items, but 'string' found at position 1.". A type is a class, or one of
|
|
563
|
+
JavaScript's type names: `'string'`, `'number'`, `'bigint'`, `'boolean'`, `'symbol'`, `'undefined'`, `'function'`,
|
|
564
|
+
`'object'`, and `'null'` and `'array'`. It returns the collection, typed with those items:
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
declare const items: Collection<unknown>
|
|
568
|
+
// ---cut---
|
|
569
|
+
const users = items.ensure(User)
|
|
570
|
+
|
|
571
|
+
const numbers = items.ensure(['number', 'string'])
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Laravel's type names are PHP's (`'int'`, `'float'`, `'bool'`); Oriel's are JavaScript's. `ensure()` doesn't stop
|
|
575
|
+
other items from being added later.
|
|
576
|
+
|
|
577
|
+
#### `every()`
|
|
578
|
+
|
|
579
|
+
`every()` tells you whether every item passes the function:
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
collect([1, 2, 3, 4]).every((value) => value > 2)
|
|
583
|
+
// false
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
An empty collection gives `true`. Given a key, a key and a value, or a key, an operator and a value, it checks each
|
|
587
|
+
item as [`where()`](#where) does, loosely.
|
|
588
|
+
|
|
589
|
+
#### `except()`
|
|
590
|
+
|
|
591
|
+
`except()` returns every item but those at the indexes you give. Laravel's takes keys:
|
|
592
|
+
|
|
593
|
+
```ts
|
|
594
|
+
collect(['a', 'b', 'c', 'd']).except(0, 2).all()
|
|
595
|
+
// ['b', 'd']
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
#### `filter()`
|
|
599
|
+
|
|
600
|
+
`filter()` keeps the items that pass the function:
|
|
601
|
+
|
|
602
|
+
```ts
|
|
603
|
+
const filtered = collect([1, 2, 3, 4]).filter((value) => value > 2)
|
|
604
|
+
|
|
605
|
+
filtered.all()
|
|
606
|
+
// [3, 4]
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Without a function, it keeps the items that are truthy in JavaScript, so an empty array stays, where PHP drops it:
|
|
610
|
+
|
|
611
|
+
```ts
|
|
612
|
+
collect([1, 2, 3, null, false, '', 0, []]).filter().all()
|
|
613
|
+
// [1, 2, 3, []]
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
The result is numbered from 0. For the opposite, see [`reject()`](#reject).
|
|
617
|
+
|
|
618
|
+
#### `first()`
|
|
619
|
+
|
|
620
|
+
`first()` returns the first item that passes the function, or the first item when you give none:
|
|
621
|
+
|
|
622
|
+
```ts
|
|
623
|
+
collect([1, 2, 3, 4]).first((value) => value > 2)
|
|
624
|
+
// 3
|
|
625
|
+
|
|
626
|
+
collect([1, 2, 3, 4]).first()
|
|
627
|
+
// 1
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
When no item passes, it returns `undefined`, or the default you give as its second argument:
|
|
631
|
+
`collect([1]).first((value) => value > 5, 'none')` is `'none'`.
|
|
632
|
+
|
|
633
|
+
#### `firstOrFail()`
|
|
634
|
+
|
|
635
|
+
`firstOrFail()` is [`first()`](#first), but throws an `ItemNotFoundError`, "Item not found.", when no item passes:
|
|
636
|
+
|
|
637
|
+
```ts
|
|
638
|
+
collect([1, 2, 3, 4]).firstOrFail((value) => value > 5)
|
|
639
|
+
// throws ItemNotFoundError
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
It also takes a key and a value, or a key, an operator and a value, compared as [`where()`](#where) does.
|
|
643
|
+
|
|
644
|
+
#### `firstWhere()`
|
|
645
|
+
|
|
646
|
+
`firstWhere()` returns the first item whose key has the value, compared loosely, or `undefined`:
|
|
647
|
+
|
|
648
|
+
```ts
|
|
649
|
+
const users = collect([
|
|
650
|
+
{ name: 'Regena', age: null },
|
|
651
|
+
{ name: 'Linda', age: 14 },
|
|
652
|
+
{ name: 'Diego', age: 23 },
|
|
653
|
+
{ name: 'Linda', age: 84 },
|
|
654
|
+
])
|
|
655
|
+
|
|
656
|
+
users.firstWhere('name', 'Linda')
|
|
657
|
+
// { name: 'Linda', age: 14 }
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
It takes an operator too, and with a key alone it finds the first item whose key is truthy:
|
|
661
|
+
|
|
662
|
+
```ts
|
|
663
|
+
declare const users: Collection<{ name: string; age: number | null }>
|
|
664
|
+
// ---cut---
|
|
665
|
+
users.firstWhere('age', '>=', 18)
|
|
666
|
+
// { name: 'Diego', age: 23 }
|
|
667
|
+
|
|
668
|
+
users.firstWhere('age')
|
|
669
|
+
// { name: 'Linda', age: 14 }
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### `flatMap()`
|
|
673
|
+
|
|
674
|
+
`flatMap()` maps each item, and flattens the results one level:
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
const collection = collect([{ name: 'Sally' }, { school: 'Arkansas' }, { age: 28 }])
|
|
678
|
+
|
|
679
|
+
const flattened = collection.flatMap((values) => Object.values(values).map((value) => String(value).toUpperCase()))
|
|
680
|
+
|
|
681
|
+
flattened.all()
|
|
682
|
+
// ['SALLY', 'ARKANSAS', '28']
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
#### `flatten()`
|
|
686
|
+
|
|
687
|
+
`flatten()` opens up nested arrays and collections, to every depth:
|
|
688
|
+
|
|
689
|
+
```ts
|
|
690
|
+
const flattened = collect([
|
|
691
|
+
['PHP', 'JavaScript'],
|
|
692
|
+
[['Ruby'], 'Go'],
|
|
693
|
+
]).flatten()
|
|
694
|
+
|
|
695
|
+
flattened.all()
|
|
696
|
+
// ['PHP', 'JavaScript', 'Ruby', 'Go']
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
Give it a depth to open fewer levels:
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
declare const collection: Collection<(string | string[])[]>
|
|
703
|
+
// ---cut---
|
|
704
|
+
collection.flatten(1).all()
|
|
705
|
+
// ['PHP', 'JavaScript', ['Ruby'], 'Go']
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
Plain objects stay as they are. Laravel flattens an associative array's values too.
|
|
709
|
+
|
|
710
|
+
#### `flip()`
|
|
711
|
+
|
|
712
|
+
`flip()` returns a plain object with each item as a key, and its index as the value:
|
|
713
|
+
|
|
714
|
+
```ts
|
|
715
|
+
collect(['taylor', 'laravel']).flip()
|
|
716
|
+
// { taylor: 0, laravel: 1 }
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
#### `forget()`
|
|
720
|
+
|
|
721
|
+
`forget()` removes the items at the indexes you give, changing the collection, and the later items move up:
|
|
722
|
+
|
|
723
|
+
```ts
|
|
724
|
+
const collection = collect(['a', 'b', 'c'])
|
|
725
|
+
|
|
726
|
+
collection.forget(1)
|
|
727
|
+
|
|
728
|
+
collection.all()
|
|
729
|
+
// ['a', 'c']
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
#### `forPage()`
|
|
733
|
+
|
|
734
|
+
`forPage()` returns the items of one page, given the page number and the number of items per page:
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
const chunk = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]).forPage(2, 3)
|
|
738
|
+
|
|
739
|
+
chunk.all()
|
|
740
|
+
// [4, 5, 6]
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
#### `fromJson()`
|
|
744
|
+
|
|
745
|
+
Static. `Collection.fromJson()` decodes a JSON string: an array gives its items, `null` an empty collection, and any
|
|
746
|
+
other value a collection of that one value:
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
Collection.fromJson('[1, 2, 3]').all()
|
|
750
|
+
// [1, 2, 3]
|
|
751
|
+
|
|
752
|
+
Collection.fromJson('{"name": "Taylor"}').all()
|
|
753
|
+
// [{ name: 'Taylor' }]
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Laravel makes a JSON object into a collection keyed by its properties.
|
|
757
|
+
|
|
758
|
+
#### `get()`
|
|
759
|
+
|
|
760
|
+
`get()` returns the item at an index, or `undefined`. Laravel's takes a key:
|
|
761
|
+
|
|
762
|
+
```ts
|
|
763
|
+
const collection = collect(['taylor', 'abigail'])
|
|
764
|
+
|
|
765
|
+
collection.get(1)
|
|
766
|
+
// 'abigail'
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
A second argument is the default, returned when there is no item at the index. A function default is called:
|
|
770
|
+
|
|
771
|
+
```ts
|
|
772
|
+
const collection = collect(['taylor'])
|
|
773
|
+
|
|
774
|
+
collection.get(5, 'default')
|
|
775
|
+
// 'default'
|
|
776
|
+
|
|
777
|
+
collection.get(5, () => 'computed')
|
|
778
|
+
// 'computed'
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
#### `groupBy()`
|
|
782
|
+
|
|
783
|
+
`groupBy()` groups the items by a key's value, and returns a plain object of collections:
|
|
784
|
+
|
|
785
|
+
```ts
|
|
786
|
+
const collection = collect([
|
|
787
|
+
{ account_id: 'account-x10', product: 'Chair' },
|
|
788
|
+
{ account_id: 'account-x10', product: 'Bookcase' },
|
|
789
|
+
{ account_id: 'account-x11', product: 'Desk' },
|
|
790
|
+
])
|
|
791
|
+
|
|
792
|
+
const grouped = collection.groupBy('account_id')
|
|
793
|
+
// {
|
|
794
|
+
// 'account-x10': [{ account_id: 'account-x10', product: 'Chair' }, { account_id: 'account-x10', product: 'Bookcase' }],
|
|
795
|
+
// 'account-x11': [{ account_id: 'account-x11', product: 'Desk' }],
|
|
796
|
+
// }
|
|
797
|
+
|
|
798
|
+
grouped['account-x11']?.count()
|
|
799
|
+
// 1
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Given a function, it groups by what the function returns:
|
|
803
|
+
|
|
804
|
+
```ts
|
|
805
|
+
declare const collection: Collection<{ account_id: string; product: string }>
|
|
806
|
+
// ---cut---
|
|
807
|
+
const grouped = collection.groupBy((item) => item.account_id.slice(-3))
|
|
808
|
+
// { x10: [...], x11: [...] }
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
Laravel's `groupBy()` also takes several keys, for nested groups, and can keep the items' keys. Oriel's takes one
|
|
812
|
+
key or function: group each group again for nested groups.
|
|
813
|
+
|
|
814
|
+
#### `has()`
|
|
815
|
+
|
|
816
|
+
`has()` tells you whether there is an item at every index you give. Laravel's takes keys:
|
|
817
|
+
|
|
818
|
+
```ts
|
|
819
|
+
const collection = collect(['Desk', 'Chair', 'Bookcase'])
|
|
820
|
+
|
|
821
|
+
collection.has(1)
|
|
822
|
+
// true
|
|
823
|
+
|
|
824
|
+
collection.has(0, 5)
|
|
825
|
+
// false
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
#### `hasAny()`
|
|
829
|
+
|
|
830
|
+
`hasAny()` tells you whether there is an item at any of the indexes you give:
|
|
831
|
+
|
|
832
|
+
```ts
|
|
833
|
+
collect(['Desk']).hasAny(0, 5)
|
|
834
|
+
// true
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
#### `implode()`
|
|
838
|
+
|
|
839
|
+
`implode()` joins the items into a string, with the glue between them:
|
|
840
|
+
|
|
841
|
+
```ts
|
|
842
|
+
collect([1, 2, 3, 4, 5]).implode('-')
|
|
843
|
+
// '1-2-3-4-5'
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
For items that are objects, give the key first, or a function:
|
|
847
|
+
|
|
848
|
+
```ts
|
|
849
|
+
const collection = collect([
|
|
850
|
+
{ account_id: 1, product: 'Desk' },
|
|
851
|
+
{ account_id: 2, product: 'Chair' },
|
|
852
|
+
])
|
|
853
|
+
|
|
854
|
+
collection.implode('product', ', ')
|
|
855
|
+
// 'Desk, Chair'
|
|
856
|
+
|
|
857
|
+
collection.implode((item) => item.product.toUpperCase(), ', ')
|
|
858
|
+
// 'DESK, CHAIR'
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
#### `intersect()`
|
|
862
|
+
|
|
863
|
+
`intersect()` keeps the items that are also in the list you give, compared strictly:
|
|
864
|
+
|
|
865
|
+
```ts
|
|
866
|
+
const intersect = collect(['Desk', 'Sofa', 'Chair']).intersect(['Desk', 'Chair', 'Bookcase'])
|
|
867
|
+
|
|
868
|
+
intersect.all()
|
|
869
|
+
// ['Desk', 'Chair']
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### `intersectUsing()`
|
|
873
|
+
|
|
874
|
+
`intersectUsing()` is [`intersect()`](#intersect) with your own comparison, a function that returns `0` for two items
|
|
875
|
+
that are the same:
|
|
876
|
+
|
|
877
|
+
```ts
|
|
878
|
+
const intersect = collect(['Desk', 'Sofa', 'Chair']).intersectUsing(['desk', 'chair', 'bookcase'], (a, b) =>
|
|
879
|
+
a.toLowerCase().localeCompare(b.toLowerCase()),
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
intersect.all()
|
|
883
|
+
// ['Desk', 'Chair']
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
#### `isEmpty()`
|
|
887
|
+
|
|
888
|
+
`isEmpty()` tells you whether the collection has no items:
|
|
889
|
+
|
|
890
|
+
```ts
|
|
891
|
+
collect([]).isEmpty()
|
|
892
|
+
// true
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
#### `isNotEmpty()`
|
|
896
|
+
|
|
897
|
+
`isNotEmpty()` tells you whether the collection has items:
|
|
898
|
+
|
|
899
|
+
```ts
|
|
900
|
+
collect([]).isNotEmpty()
|
|
901
|
+
// false
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
#### `join()`
|
|
905
|
+
|
|
906
|
+
`join()` joins the items into a string, with the glue between them, and another glue before the last one if you give
|
|
907
|
+
it:
|
|
908
|
+
|
|
909
|
+
```ts
|
|
910
|
+
collect(['a', 'b', 'c']).join(', ')
|
|
911
|
+
// 'a, b, c'
|
|
912
|
+
|
|
913
|
+
collect(['a', 'b', 'c']).join(', ', ', and ')
|
|
914
|
+
// 'a, b, and c'
|
|
915
|
+
|
|
916
|
+
collect(['a', 'b']).join(', ', ' and ')
|
|
917
|
+
// 'a and b'
|
|
918
|
+
|
|
919
|
+
collect(['a']).join(', ', ' and ')
|
|
920
|
+
// 'a'
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
#### `keyBy()`
|
|
924
|
+
|
|
925
|
+
`keyBy()` returns a plain object of the items, under a key's value. When two items have the same value, the last one
|
|
926
|
+
stays:
|
|
927
|
+
|
|
928
|
+
```ts
|
|
929
|
+
const collection = collect([
|
|
930
|
+
{ product_id: 'prod-100', name: 'Desk' },
|
|
931
|
+
{ product_id: 'prod-200', name: 'Chair' },
|
|
932
|
+
])
|
|
933
|
+
|
|
934
|
+
collection.keyBy('product_id')
|
|
935
|
+
// {
|
|
936
|
+
// 'prod-100': { product_id: 'prod-100', name: 'Desk' },
|
|
937
|
+
// 'prod-200': { product_id: 'prod-200', name: 'Chair' },
|
|
938
|
+
// }
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
It takes a function too: `collection.keyBy((item) => item.product_id.toUpperCase())`.
|
|
942
|
+
|
|
943
|
+
#### `keys()`
|
|
944
|
+
|
|
945
|
+
`keys()` returns the indexes, from 0:
|
|
946
|
+
|
|
947
|
+
```ts
|
|
948
|
+
collect(['a', 'b', 'c']).keys().all()
|
|
949
|
+
// [0, 1, 2]
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
#### `last()`
|
|
953
|
+
|
|
954
|
+
`last()` returns the last item that passes the function, or the last item when you give none, or `undefined`:
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
collect([1, 2, 3, 4]).last((value) => value < 3)
|
|
958
|
+
// 2
|
|
959
|
+
|
|
960
|
+
collect([1, 2, 3, 4]).last()
|
|
961
|
+
// 4
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
Like [`first()`](#first), it takes a default as its second argument.
|
|
965
|
+
|
|
966
|
+
#### `lazy()`
|
|
967
|
+
|
|
968
|
+
`lazy()` returns a [lazy collection](#lazy-collections) of a copy of the items, so later changes to this collection
|
|
969
|
+
don't reach it:
|
|
970
|
+
|
|
971
|
+
```ts
|
|
972
|
+
const lazyCollection = collect([1, 2, 3, 4]).lazy()
|
|
973
|
+
|
|
974
|
+
const evens = await lazyCollection.filter((value) => value % 2 === 0).collect()
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
#### `make()`
|
|
978
|
+
|
|
979
|
+
Static. `Collection.make()` makes a collection, as `collect()` does.
|
|
980
|
+
|
|
981
|
+
#### `map()`
|
|
982
|
+
|
|
983
|
+
`map()` calls the function with each item and its index, and returns a collection of the results:
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
const multiplied = collect([1, 2, 3, 4, 5]).map((item) => item * 2)
|
|
987
|
+
|
|
988
|
+
multiplied.all()
|
|
989
|
+
// [2, 4, 6, 8, 10]
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
To change the collection itself, use [`transform()`](#transform).
|
|
993
|
+
|
|
994
|
+
#### `mapInto()`
|
|
995
|
+
|
|
996
|
+
`mapInto()` makes an instance of a class for each item, with `new` and the item, then the index:
|
|
997
|
+
|
|
998
|
+
```ts
|
|
999
|
+
class Currency {
|
|
1000
|
+
constructor(public code: string) {}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const currencies = collect(['USD', 'EUR', 'GBP']).mapInto(Currency)
|
|
1004
|
+
|
|
1005
|
+
currencies.all()
|
|
1006
|
+
// [Currency { code: 'USD' }, Currency { code: 'EUR' }, Currency { code: 'GBP' }]
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
#### `mapSpread()`
|
|
1010
|
+
|
|
1011
|
+
`mapSpread()` is [`map()`](#map) for a collection of lists: each list's items are the function's arguments, followed
|
|
1012
|
+
by the index:
|
|
1013
|
+
|
|
1014
|
+
```ts
|
|
1015
|
+
const sequence = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
1016
|
+
.chunk(2)
|
|
1017
|
+
.mapSpread((even, odd) => even + odd)
|
|
1018
|
+
|
|
1019
|
+
sequence.all()
|
|
1020
|
+
// [1, 5, 9, 13, 17]
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
#### `mapToDictionary()`
|
|
1024
|
+
|
|
1025
|
+
`mapToDictionary()` is [`mapToGroups()`](#maptogroups) with arrays for the groups in place of collections:
|
|
1026
|
+
|
|
1027
|
+
```ts
|
|
1028
|
+
const collection = collect([
|
|
1029
|
+
{ name: 'John Doe', department: 'Sales' },
|
|
1030
|
+
{ name: 'Jane Doe', department: 'Sales' },
|
|
1031
|
+
])
|
|
1032
|
+
|
|
1033
|
+
collection.mapToDictionary((item) => [item.department, item.name])
|
|
1034
|
+
// { Sales: ['John Doe', 'Jane Doe'] }
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
#### `mapToGroups()`
|
|
1038
|
+
|
|
1039
|
+
`mapToGroups()` groups the items by the function's results. The function returns a `[key, value]` pair, and the
|
|
1040
|
+
result is a plain object of collections:
|
|
1041
|
+
|
|
1042
|
+
```ts
|
|
1043
|
+
const collection = collect([
|
|
1044
|
+
{ name: 'John Doe', department: 'Sales' },
|
|
1045
|
+
{ name: 'Jane Doe', department: 'Sales' },
|
|
1046
|
+
{ name: 'Johnny Doe', department: 'Marketing' },
|
|
1047
|
+
])
|
|
1048
|
+
|
|
1049
|
+
const grouped = collection.mapToGroups((item) => [item.department, item.name])
|
|
1050
|
+
// { Sales: ['John Doe', 'Jane Doe'], Marketing: ['Johnny Doe'] }
|
|
1051
|
+
|
|
1052
|
+
grouped.Sales?.all()
|
|
1053
|
+
// ['John Doe', 'Jane Doe']
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
Laravel's function returns a one-entry array, `[$key => $value]`.
|
|
1057
|
+
|
|
1058
|
+
#### `mapWithKeys()`
|
|
1059
|
+
|
|
1060
|
+
`mapWithKeys()` builds a plain object from the function's results, each a `[key, value]` pair:
|
|
1061
|
+
|
|
1062
|
+
```ts
|
|
1063
|
+
const collection = collect([
|
|
1064
|
+
{ name: 'John', department: 'Sales', email: 'john@example.com' },
|
|
1065
|
+
{ name: 'Jane', department: 'Marketing', email: 'jane@example.com' },
|
|
1066
|
+
])
|
|
1067
|
+
|
|
1068
|
+
collection.mapWithKeys((item) => [item.email, item.name])
|
|
1069
|
+
// { 'john@example.com': 'John', 'jane@example.com': 'Jane' }
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
Laravel's function returns a one-entry array, and the result is a collection.
|
|
1073
|
+
|
|
1074
|
+
#### `max()`
|
|
1075
|
+
|
|
1076
|
+
`max()` returns the largest number among the items, or a key's values, or `undefined` when there is none:
|
|
1077
|
+
|
|
1078
|
+
```ts
|
|
1079
|
+
collect([{ foo: 10 }, { foo: 20 }]).max('foo')
|
|
1080
|
+
// 20
|
|
1081
|
+
|
|
1082
|
+
collect([1, 2, 3, 4, 5]).max()
|
|
1083
|
+
// 5
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
Only numbers count. Laravel's `max()` compares other values too.
|
|
1087
|
+
|
|
1088
|
+
#### `median()`
|
|
1089
|
+
|
|
1090
|
+
`median()` returns the middle number, or the average of the two middle ones, of the items or a key's values:
|
|
1091
|
+
|
|
1092
|
+
```ts
|
|
1093
|
+
collect([{ foo: 10 }, { foo: 10 }, { foo: 20 }, { foo: 40 }]).median('foo')
|
|
1094
|
+
// 15
|
|
1095
|
+
|
|
1096
|
+
collect([1, 1, 2, 4]).median()
|
|
1097
|
+
// 1.5
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
Only numbers count, and a collection with none gives `undefined`.
|
|
1101
|
+
|
|
1102
|
+
#### `merge()`
|
|
1103
|
+
|
|
1104
|
+
`merge()` returns the items followed by the ones you give, as [`concat()`](#concat) does:
|
|
1105
|
+
|
|
1106
|
+
```ts
|
|
1107
|
+
const merged = collect(['Desk', 'Chair']).merge(['Bookcase', 'Door'])
|
|
1108
|
+
|
|
1109
|
+
merged.all()
|
|
1110
|
+
// ['Desk', 'Chair', 'Bookcase', 'Door']
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
Laravel's `merge()` overwrites the items of string keys; a list has none.
|
|
1114
|
+
|
|
1115
|
+
#### `min()`
|
|
1116
|
+
|
|
1117
|
+
`min()` returns the smallest number among the items, or a key's values, or `undefined` when there is none:
|
|
1118
|
+
|
|
1119
|
+
```ts
|
|
1120
|
+
collect([{ foo: 10 }, { foo: 20 }]).min('foo')
|
|
1121
|
+
// 10
|
|
1122
|
+
|
|
1123
|
+
collect([1, 2, 3, 4, 5]).min()
|
|
1124
|
+
// 1
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
#### `mode()`
|
|
1128
|
+
|
|
1129
|
+
`mode()` returns the most frequent values, in an array, in the order they first appear, or `undefined` for an empty
|
|
1130
|
+
collection:
|
|
1131
|
+
|
|
1132
|
+
```ts
|
|
1133
|
+
collect([{ foo: 10 }, { foo: 10 }, { foo: 20 }, { foo: 40 }]).mode('foo')
|
|
1134
|
+
// [10]
|
|
1135
|
+
|
|
1136
|
+
collect([1, 1, 2, 4]).mode()
|
|
1137
|
+
// [1]
|
|
1138
|
+
|
|
1139
|
+
collect([1, 1, 2, 2]).mode()
|
|
1140
|
+
// [1, 2]
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
#### `multiply()`
|
|
1144
|
+
|
|
1145
|
+
`multiply()` repeats the items the given number of times:
|
|
1146
|
+
|
|
1147
|
+
```ts
|
|
1148
|
+
const users = collect([
|
|
1149
|
+
{ name: 'User #1', email: 'user1@example.com' },
|
|
1150
|
+
{ name: 'User #2', email: 'user2@example.com' },
|
|
1151
|
+
]).multiply(3)
|
|
1152
|
+
|
|
1153
|
+
users.count()
|
|
1154
|
+
// 6
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
#### `nth()`
|
|
1158
|
+
|
|
1159
|
+
`nth()` returns every nth item, from the first, or from the offset you give:
|
|
1160
|
+
|
|
1161
|
+
```ts
|
|
1162
|
+
const collection = collect(['a', 'b', 'c', 'd', 'e', 'f'])
|
|
1163
|
+
|
|
1164
|
+
collection.nth(4).all()
|
|
1165
|
+
// ['a', 'e']
|
|
1166
|
+
|
|
1167
|
+
collection.nth(4, 1).all()
|
|
1168
|
+
// ['b', 'f']
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
#### `only()`
|
|
1172
|
+
|
|
1173
|
+
`only()` returns the items at the indexes you give, in the collection's order. Laravel's takes keys:
|
|
1174
|
+
|
|
1175
|
+
```ts
|
|
1176
|
+
collect(['a', 'b', 'c', 'd']).only(0, 2).all()
|
|
1177
|
+
// ['a', 'c']
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
#### `pad()`
|
|
1181
|
+
|
|
1182
|
+
`pad()` fills the collection with the value up to the given size. A negative size pads at the start, and a size no
|
|
1183
|
+
larger than the collection changes nothing:
|
|
1184
|
+
|
|
1185
|
+
```ts
|
|
1186
|
+
const collection = collect(['A', 'B', 'C'])
|
|
1187
|
+
|
|
1188
|
+
collection.pad(5, 0).all()
|
|
1189
|
+
// ['A', 'B', 'C', 0, 0]
|
|
1190
|
+
|
|
1191
|
+
collection.pad(-5, 0).all()
|
|
1192
|
+
// [0, 0, 'A', 'B', 'C']
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
#### `partition()`
|
|
1196
|
+
|
|
1197
|
+
`partition()` returns two collections: the items that pass the function, and the ones that don't:
|
|
1198
|
+
|
|
1199
|
+
```ts
|
|
1200
|
+
const [underThree, equalOrAboveThree] = collect([1, 2, 3, 4, 5, 6]).partition((item) => item < 3)
|
|
1201
|
+
|
|
1202
|
+
underThree.all()
|
|
1203
|
+
// [1, 2]
|
|
1204
|
+
|
|
1205
|
+
equalOrAboveThree.all()
|
|
1206
|
+
// [3, 4, 5, 6]
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
#### `percentage()`
|
|
1210
|
+
|
|
1211
|
+
`percentage()` returns the share of the items that pass the function, as a percentage, rounded to two decimal places
|
|
1212
|
+
or the number you give, or `undefined` for an empty collection:
|
|
1213
|
+
|
|
1214
|
+
```ts
|
|
1215
|
+
const collection = collect([1, 1, 2, 2, 2, 3])
|
|
1216
|
+
|
|
1217
|
+
collection.percentage((value) => value === 1)
|
|
1218
|
+
// 33.33
|
|
1219
|
+
|
|
1220
|
+
collection.percentage((value) => value === 1, 3)
|
|
1221
|
+
// 33.333
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
#### `pipe()`
|
|
1225
|
+
|
|
1226
|
+
`pipe()` passes the collection to the function, and returns what it returns:
|
|
1227
|
+
|
|
1228
|
+
```ts
|
|
1229
|
+
collect([1, 2, 3]).pipe((collection) => collection.sum())
|
|
1230
|
+
// 6
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
#### `pipeInto()`
|
|
1234
|
+
|
|
1235
|
+
`pipeInto()` makes an instance of a class with the collection, `new` and the collection:
|
|
1236
|
+
|
|
1237
|
+
```ts
|
|
1238
|
+
class ResourceCollection {
|
|
1239
|
+
constructor(public collection: Collection<number>) {}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
const resource = collect([1, 2, 3]).pipeInto(ResourceCollection)
|
|
1243
|
+
|
|
1244
|
+
resource.collection.all()
|
|
1245
|
+
// [1, 2, 3]
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
#### `pipeThrough()`
|
|
1249
|
+
|
|
1250
|
+
`pipeThrough()` passes the collection to the first function, its result to the next, and so on, and returns the last
|
|
1251
|
+
result:
|
|
1252
|
+
|
|
1253
|
+
```ts
|
|
1254
|
+
const result = collect([1, 2, 3]).pipeThrough([
|
|
1255
|
+
(collection) => collection.merge([4, 5]),
|
|
1256
|
+
(collection) => collection.sum(),
|
|
1257
|
+
])
|
|
1258
|
+
// 15
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
#### `pluck()`
|
|
1262
|
+
|
|
1263
|
+
`pluck()` returns a key's values:
|
|
1264
|
+
|
|
1265
|
+
```ts
|
|
1266
|
+
const collection = collect([
|
|
1267
|
+
{ product_id: 'prod-100', name: 'Desk' },
|
|
1268
|
+
{ product_id: 'prod-200', name: 'Chair' },
|
|
1269
|
+
])
|
|
1270
|
+
|
|
1271
|
+
collection.pluck('name').all()
|
|
1272
|
+
// ['Desk', 'Chair']
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
Laravel's `pluck()` also takes a second key, for the result's keys, and dot notation for nested values. Oriel's takes
|
|
1276
|
+
one key: for a dictionary of values, use [`mapWithKeys()`](#mapwithkeys), and for a nested value, `map()`.
|
|
1277
|
+
|
|
1278
|
+
#### `pop()`
|
|
1279
|
+
|
|
1280
|
+
`pop()` removes the last item and returns it, changing the collection:
|
|
1281
|
+
|
|
1282
|
+
```ts
|
|
1283
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1284
|
+
|
|
1285
|
+
collection.pop()
|
|
1286
|
+
// 5
|
|
1287
|
+
|
|
1288
|
+
collection.all()
|
|
1289
|
+
// [1, 2, 3, 4]
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
Given a number, it removes that many from the end and returns them in a collection, the last first:
|
|
1293
|
+
|
|
1294
|
+
```ts
|
|
1295
|
+
collect([1, 2, 3, 4, 5]).pop(3).all()
|
|
1296
|
+
// [5, 4, 3]
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
Laravel's `pop(1)` returns the item; Oriel's returns a collection whenever you give a number.
|
|
1300
|
+
|
|
1301
|
+
#### `prepend()`
|
|
1302
|
+
|
|
1303
|
+
`prepend()` adds an item at the start, changing the collection:
|
|
1304
|
+
|
|
1305
|
+
```ts
|
|
1306
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1307
|
+
|
|
1308
|
+
collection.prepend(0)
|
|
1309
|
+
|
|
1310
|
+
collection.all()
|
|
1311
|
+
// [0, 1, 2, 3, 4, 5]
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
Laravel's takes a key as its second argument; a list has none.
|
|
1315
|
+
|
|
1316
|
+
#### `pull()`
|
|
1317
|
+
|
|
1318
|
+
`pull()` removes the item at an index and returns it, changing the collection, and the later items move up:
|
|
1319
|
+
|
|
1320
|
+
```ts
|
|
1321
|
+
const collection = collect(['prod-100', 'Desk'])
|
|
1322
|
+
|
|
1323
|
+
collection.pull(1)
|
|
1324
|
+
// 'Desk'
|
|
1325
|
+
|
|
1326
|
+
collection.all()
|
|
1327
|
+
// ['prod-100']
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
When there is no item at the index, it returns `undefined`, or the default you give.
|
|
1331
|
+
|
|
1332
|
+
#### `push()`
|
|
1333
|
+
|
|
1334
|
+
`push()` adds items at the end, changing the collection:
|
|
1335
|
+
|
|
1336
|
+
```ts
|
|
1337
|
+
const collection = collect([1, 2, 3, 4])
|
|
1338
|
+
|
|
1339
|
+
collection.push(5)
|
|
1340
|
+
|
|
1341
|
+
collection.all()
|
|
1342
|
+
// [1, 2, 3, 4, 5]
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
It takes several items: `collection.push(6, 7)`.
|
|
1346
|
+
|
|
1347
|
+
#### `put()`
|
|
1348
|
+
|
|
1349
|
+
`put()` sets the item at an index, changing the collection. An index past the end adds the item at the end, and a
|
|
1350
|
+
negative or fractional index throws a `RangeError`:
|
|
1351
|
+
|
|
1352
|
+
```ts
|
|
1353
|
+
const collection = collect(['Desk', 'Chair'])
|
|
1354
|
+
|
|
1355
|
+
collection.put(1, 'Sofa')
|
|
1356
|
+
|
|
1357
|
+
collection.all()
|
|
1358
|
+
// ['Desk', 'Sofa']
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
#### `random()`
|
|
1362
|
+
|
|
1363
|
+
`random()` returns a random item:
|
|
1364
|
+
|
|
1365
|
+
```ts
|
|
1366
|
+
collect([1, 2, 3, 4, 5]).random()
|
|
1367
|
+
// 4 (retrieved randomly)
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
Given a number, it returns a collection of that many items, picked at random and kept in their order. A function
|
|
1371
|
+
receives the collection and returns the number. Asking for more items than the collection has throws a `RangeError`:
|
|
1372
|
+
"You requested 3 items, but there are only 2 items available.".
|
|
1373
|
+
|
|
1374
|
+
```ts
|
|
1375
|
+
collect([1, 2, 3, 4, 5]).random(3).all()
|
|
1376
|
+
// [2, 4, 5] (retrieved randomly)
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
#### `range()`
|
|
1380
|
+
|
|
1381
|
+
Static. `Collection.range()` returns the numbers from the first to the last, both included, counting by 1 or by the
|
|
1382
|
+
step you give:
|
|
1383
|
+
|
|
1384
|
+
```ts
|
|
1385
|
+
Collection.range(3, 6).all()
|
|
1386
|
+
// [3, 4, 5, 6]
|
|
1387
|
+
|
|
1388
|
+
Collection.range(10, 0, 5).all()
|
|
1389
|
+
// [10, 5, 0]
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
It takes numbers only, where PHP's `range()` also counts letters.
|
|
1393
|
+
|
|
1394
|
+
#### `reduce()`
|
|
1395
|
+
|
|
1396
|
+
`reduce()` reduces the collection to one value, passing the result of each step to the next. The function receives
|
|
1397
|
+
the result so far, the item and its index, and the second argument is the first result:
|
|
1398
|
+
|
|
1399
|
+
```ts
|
|
1400
|
+
collect([1, 2, 3]).reduce((carry, item) => carry + item, 0)
|
|
1401
|
+
// 6
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
Laravel's starting value is optional, and `null` when you leave it out; Oriel's is required.
|
|
1405
|
+
|
|
1406
|
+
#### `reduceSpread()`
|
|
1407
|
+
|
|
1408
|
+
`reduceSpread()` reduces the collection to several values at once. The function receives them, then the item and
|
|
1409
|
+
its index, and returns them in an array; the arguments after the function are their starting values:
|
|
1410
|
+
|
|
1411
|
+
```ts
|
|
1412
|
+
const [creditsLeft, prices] = collect([{ price: 3 }, { price: 6 }]).reduceSpread(
|
|
1413
|
+
(credits: number, batch: number[], item) => [credits - item.price, [...batch, item.price]],
|
|
1414
|
+
10,
|
|
1415
|
+
[] as number[],
|
|
1416
|
+
)
|
|
1417
|
+
// creditsLeft: 1, prices: [3, 6]
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
A function that doesn't return an array throws a `TypeError`.
|
|
1421
|
+
|
|
1422
|
+
#### `reject()`
|
|
1423
|
+
|
|
1424
|
+
`reject()` drops the items that pass the function, the opposite of [`filter()`](#filter):
|
|
1425
|
+
|
|
1426
|
+
```ts
|
|
1427
|
+
const filtered = collect([1, 2, 3, 4]).reject((value) => value > 2)
|
|
1428
|
+
|
|
1429
|
+
filtered.all()
|
|
1430
|
+
// [1, 2]
|
|
1431
|
+
```
|
|
1432
|
+
|
|
1433
|
+
#### `replace()`
|
|
1434
|
+
|
|
1435
|
+
`replace()` returns a copy with the items at some indexes replaced. Give it an object of indexes and values, or an
|
|
1436
|
+
array, which replaces from index 0. An index past the end adds the item at the end, and a negative or fractional
|
|
1437
|
+
index throws a `RangeError`:
|
|
1438
|
+
|
|
1439
|
+
```ts
|
|
1440
|
+
const collection = collect(['Taylor', 'Abigail', 'James'])
|
|
1441
|
+
|
|
1442
|
+
collection.replace({ 1: 'Victoria', 3: 'Finn' }).all()
|
|
1443
|
+
// ['Taylor', 'Victoria', 'James', 'Finn']
|
|
1444
|
+
|
|
1445
|
+
collection.replace(['Victoria']).all()
|
|
1446
|
+
// ['Victoria', 'Abigail', 'James']
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
#### `reverse()`
|
|
1450
|
+
|
|
1451
|
+
`reverse()` returns the items in the reverse order:
|
|
1452
|
+
|
|
1453
|
+
```ts
|
|
1454
|
+
collect(['a', 'b', 'c', 'd', 'e']).reverse().all()
|
|
1455
|
+
// ['e', 'd', 'c', 'b', 'a']
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
Laravel's keeps each item's key; Oriel's numbers them from 0.
|
|
1459
|
+
|
|
1460
|
+
#### `search()`
|
|
1461
|
+
|
|
1462
|
+
`search()` returns the index of the first item that is the value, compared loosely, or `false` when there is none:
|
|
1463
|
+
|
|
1464
|
+
```ts
|
|
1465
|
+
const collection = collect([2, 4, 6, 8])
|
|
1466
|
+
|
|
1467
|
+
collection.search(4)
|
|
1468
|
+
// 1
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
Pass `true` to compare strictly, or give a function:
|
|
1472
|
+
|
|
1473
|
+
```ts
|
|
1474
|
+
declare const collection: Collection<number | string>
|
|
1475
|
+
// ---cut---
|
|
1476
|
+
collection.search('4', true)
|
|
1477
|
+
// false
|
|
1478
|
+
|
|
1479
|
+
collection.search((item) => Number(item) > 5)
|
|
1480
|
+
// 2
|
|
1481
|
+
```
|
|
1482
|
+
|
|
1483
|
+
#### `select()`
|
|
1484
|
+
|
|
1485
|
+
`select()` keeps only the keys you give of each item, as a SQL `SELECT` does:
|
|
1486
|
+
|
|
1487
|
+
```ts
|
|
1488
|
+
const users = collect([
|
|
1489
|
+
{ name: 'Taylor Otwell', role: 'Developer', status: 'active' },
|
|
1490
|
+
{ name: 'Victoria Faith', role: 'Researcher', status: 'active' },
|
|
1491
|
+
])
|
|
1492
|
+
|
|
1493
|
+
users.select(['name', 'role']).all()
|
|
1494
|
+
// [{ name: 'Taylor Otwell', role: 'Developer' }, { name: 'Victoria Faith', role: 'Researcher' }]
|
|
1495
|
+
```
|
|
1496
|
+
|
|
1497
|
+
#### `shift()`
|
|
1498
|
+
|
|
1499
|
+
`shift()` removes the first item and returns it, changing the collection:
|
|
1500
|
+
|
|
1501
|
+
```ts
|
|
1502
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1503
|
+
|
|
1504
|
+
collection.shift()
|
|
1505
|
+
// 1
|
|
1506
|
+
|
|
1507
|
+
collection.all()
|
|
1508
|
+
// [2, 3, 4, 5]
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1511
|
+
Given a number, it removes that many from the start and returns them in a collection. A negative number throws a
|
|
1512
|
+
`RangeError`. As with [`pop()`](#pop), `shift(1)` returns a collection.
|
|
1513
|
+
|
|
1514
|
+
#### `shuffle()`
|
|
1515
|
+
|
|
1516
|
+
`shuffle()` returns the items in a random order:
|
|
1517
|
+
|
|
1518
|
+
```ts
|
|
1519
|
+
collect([1, 2, 3, 4, 5]).shuffle().all()
|
|
1520
|
+
// [3, 2, 5, 1, 4] (generated randomly)
|
|
1521
|
+
```
|
|
1522
|
+
|
|
1523
|
+
#### `skip()`
|
|
1524
|
+
|
|
1525
|
+
`skip()` returns the items after the given number of them:
|
|
1526
|
+
|
|
1527
|
+
```ts
|
|
1528
|
+
collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).skip(4).all()
|
|
1529
|
+
// [5, 6, 7, 8, 9, 10]
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
#### `skipUntil()`
|
|
1533
|
+
|
|
1534
|
+
`skipUntil()` skips items until one passes the function, or is the value, compared strictly, and returns the rest,
|
|
1535
|
+
that one included. When none does, the result is empty:
|
|
1536
|
+
|
|
1537
|
+
```ts
|
|
1538
|
+
collect([1, 2, 3, 4]).skipUntil((item) => item >= 3).all()
|
|
1539
|
+
// [3, 4]
|
|
1540
|
+
|
|
1541
|
+
collect([1, 2, 3, 4]).skipUntil(3).all()
|
|
1542
|
+
// [3, 4]
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
#### `skipWhile()`
|
|
1546
|
+
|
|
1547
|
+
`skipWhile()` skips items while they pass the function, or are the value, and returns the rest:
|
|
1548
|
+
|
|
1549
|
+
```ts
|
|
1550
|
+
collect([1, 2, 3, 4]).skipWhile((item) => item <= 3).all()
|
|
1551
|
+
// [4]
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
#### `slice()`
|
|
1555
|
+
|
|
1556
|
+
`slice()` returns the items from an index on, or as many as you give:
|
|
1557
|
+
|
|
1558
|
+
```ts
|
|
1559
|
+
const collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
1560
|
+
|
|
1561
|
+
collection.slice(4).all()
|
|
1562
|
+
// [5, 6, 7, 8, 9, 10]
|
|
1563
|
+
|
|
1564
|
+
collection.slice(4, 2).all()
|
|
1565
|
+
// [5, 6]
|
|
1566
|
+
```
|
|
1567
|
+
|
|
1568
|
+
It counts as PHP's `array_slice()` does, which Laravel's uses: a negative start counts from the end, and a negative
|
|
1569
|
+
length leaves that many items off the end:
|
|
1570
|
+
|
|
1571
|
+
```ts
|
|
1572
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1573
|
+
|
|
1574
|
+
collection.slice(-2).all()
|
|
1575
|
+
// [4, 5]
|
|
1576
|
+
|
|
1577
|
+
collection.slice(1, -1).all()
|
|
1578
|
+
// [2, 3, 4]
|
|
1579
|
+
```
|
|
1580
|
+
|
|
1581
|
+
JavaScript's `Array.slice()` takes an end index instead of a length.
|
|
1582
|
+
|
|
1583
|
+
#### `sliding()`
|
|
1584
|
+
|
|
1585
|
+
`sliding()` returns overlapping windows of the items, two at a time or as many as you give, each starting one item, or
|
|
1586
|
+
the step you give, after the last:
|
|
1587
|
+
|
|
1588
|
+
```ts
|
|
1589
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1590
|
+
|
|
1591
|
+
collection.sliding(2).toArray()
|
|
1592
|
+
// [[1, 2], [2, 3], [3, 4], [4, 5]]
|
|
1593
|
+
|
|
1594
|
+
collection.sliding(3, 2).toArray()
|
|
1595
|
+
// [[1, 2, 3], [3, 4, 5]]
|
|
1596
|
+
```
|
|
1597
|
+
|
|
1598
|
+
#### `sole()`
|
|
1599
|
+
|
|
1600
|
+
`sole()` returns the only item that passes the function, and throws when there isn't exactly one:
|
|
1601
|
+
|
|
1602
|
+
```ts
|
|
1603
|
+
collect([1, 2, 3, 4]).sole((value) => value === 2)
|
|
1604
|
+
// 2
|
|
1605
|
+
```
|
|
1606
|
+
|
|
1607
|
+
It also takes a key and a value, compared loosely, or a key, an operator and a value, and with nothing it returns the
|
|
1608
|
+
collection's only item:
|
|
1609
|
+
|
|
1610
|
+
```ts
|
|
1611
|
+
const collection = collect([
|
|
1612
|
+
{ product: 'Desk', price: 200 },
|
|
1613
|
+
{ product: 'Chair', price: 100 },
|
|
1614
|
+
])
|
|
1615
|
+
|
|
1616
|
+
collection.sole('product', 'Chair')
|
|
1617
|
+
// { product: 'Chair', price: 100 }
|
|
1618
|
+
```
|
|
1619
|
+
|
|
1620
|
+
No match throws an `ItemNotFoundError`, "Item not found.", and more than one a `MultipleItemsFoundError`, "2 items
|
|
1621
|
+
were found.".
|
|
1622
|
+
|
|
1623
|
+
#### `some()`
|
|
1624
|
+
|
|
1625
|
+
Another name for [`contains()`](#contains).
|
|
1626
|
+
|
|
1627
|
+
#### `sort()`
|
|
1628
|
+
|
|
1629
|
+
`sort()` returns the items sorted, in ascending order: numbers by their value, dates by their time, and anything else
|
|
1630
|
+
as text, by `localeCompare()`. `null` and `undefined` come last:
|
|
1631
|
+
|
|
1632
|
+
```ts
|
|
1633
|
+
collect([5, 3, 1, 2, 4]).sort().all()
|
|
1634
|
+
// [1, 2, 3, 4, 5]
|
|
1635
|
+
|
|
1636
|
+
collect([10, 9, 1]).sort().all()
|
|
1637
|
+
// [1, 9, 10]
|
|
1638
|
+
```
|
|
1639
|
+
|
|
1640
|
+
Give it a comparison function for another order, as you would to an array's `sort()`:
|
|
1641
|
+
|
|
1642
|
+
```ts
|
|
1643
|
+
collect([5, 3, 1, 2, 4])
|
|
1644
|
+
.sort((a, b) => b - a)
|
|
1645
|
+
.all()
|
|
1646
|
+
// [5, 4, 3, 2, 1]
|
|
1647
|
+
```
|
|
1648
|
+
|
|
1649
|
+
Unlike an array's, it leaves the collection as it was. To sort objects, see [`sortBy()`](#sortby).
|
|
1650
|
+
|
|
1651
|
+
#### `sortBy()`
|
|
1652
|
+
|
|
1653
|
+
`sortBy()` sorts the items by a key's value, or by what a function returns:
|
|
1654
|
+
|
|
1655
|
+
```ts
|
|
1656
|
+
const collection = collect([
|
|
1657
|
+
{ name: 'Desk', price: 200 },
|
|
1658
|
+
{ name: 'Chair', price: 100 },
|
|
1659
|
+
{ name: 'Bookcase', price: 150 },
|
|
1660
|
+
])
|
|
1661
|
+
|
|
1662
|
+
collection.sortBy('price').pluck('name').all()
|
|
1663
|
+
// ['Chair', 'Bookcase', 'Desk']
|
|
1664
|
+
|
|
1665
|
+
collection.sortBy((product) => product.name.length).pluck('name').all()
|
|
1666
|
+
// ['Desk', 'Chair', 'Bookcase']
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
The second argument is the direction, `'asc'` or `'desc'`. To sort by several keys, give a list of keys, each with its
|
|
1670
|
+
direction if you like, or of comparison functions:
|
|
1671
|
+
|
|
1672
|
+
```ts
|
|
1673
|
+
const collection = collect([
|
|
1674
|
+
{ name: 'Taylor Otwell', age: 34 },
|
|
1675
|
+
{ name: 'Abigail Otwell', age: 30 },
|
|
1676
|
+
{ name: 'Taylor Otwell', age: 36 },
|
|
1677
|
+
{ name: 'Abigail Otwell', age: 32 },
|
|
1678
|
+
])
|
|
1679
|
+
|
|
1680
|
+
collection.sortBy([['name', 'asc'], ['age', 'desc']]).all()
|
|
1681
|
+
// [
|
|
1682
|
+
// { name: 'Abigail Otwell', age: 32 },
|
|
1683
|
+
// { name: 'Abigail Otwell', age: 30 },
|
|
1684
|
+
// { name: 'Taylor Otwell', age: 36 },
|
|
1685
|
+
// { name: 'Taylor Otwell', age: 34 },
|
|
1686
|
+
// ]
|
|
1687
|
+
|
|
1688
|
+
collection.sortBy([(a, b) => a.name.localeCompare(b.name), (a, b) => b.age - a.age]).all()
|
|
1689
|
+
```
|
|
1690
|
+
|
|
1691
|
+
Items with the same value keep their order. A `null` or missing value comes last, in both directions, where Laravel
|
|
1692
|
+
puts it first in ascending order.
|
|
1693
|
+
|
|
1694
|
+
#### `sortByDesc()`
|
|
1695
|
+
|
|
1696
|
+
`sortByDesc()` is [`sortBy()`](#sortby) in descending order.
|
|
1697
|
+
|
|
1698
|
+
#### `sortDesc()`
|
|
1699
|
+
|
|
1700
|
+
`sortDesc()` is [`sort()`](#sort) in descending order:
|
|
1701
|
+
|
|
1702
|
+
```ts
|
|
1703
|
+
collect([5, 3, 1, 2, 4]).sortDesc().all()
|
|
1704
|
+
// [5, 4, 3, 2, 1]
|
|
1705
|
+
```
|
|
1706
|
+
|
|
1707
|
+
#### `splice()`
|
|
1708
|
+
|
|
1709
|
+
`splice()` removes the items from an index on and returns them, changing the collection:
|
|
1710
|
+
|
|
1711
|
+
```ts
|
|
1712
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1713
|
+
|
|
1714
|
+
const chunk = collection.splice(2)
|
|
1715
|
+
|
|
1716
|
+
chunk.all()
|
|
1717
|
+
// [3, 4, 5]
|
|
1718
|
+
|
|
1719
|
+
collection.all()
|
|
1720
|
+
// [1, 2]
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
A second argument limits how many it removes, and a third is a list of items to put in their place. As in PHP, a
|
|
1724
|
+
negative index counts from the end, and a negative number leaves that many items at the end:
|
|
1725
|
+
|
|
1726
|
+
```ts
|
|
1727
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1728
|
+
|
|
1729
|
+
const chunk = collection.splice(2, 1, [10, 11])
|
|
1730
|
+
|
|
1731
|
+
chunk.all()
|
|
1732
|
+
// [3]
|
|
1733
|
+
|
|
1734
|
+
collection.all()
|
|
1735
|
+
// [1, 2, 10, 11, 4, 5]
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
#### `split()`
|
|
1739
|
+
|
|
1740
|
+
`split()` splits the collection into the given number of groups, as even as they can be, the first ones larger:
|
|
1741
|
+
|
|
1742
|
+
```ts
|
|
1743
|
+
collect([1, 2, 3, 4, 5]).split(3).toArray()
|
|
1744
|
+
// [[1, 2], [3, 4], [5]]
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
#### `splitIn()`
|
|
1748
|
+
|
|
1749
|
+
`splitIn()` splits the collection into the given number of groups, every group full except the last:
|
|
1750
|
+
|
|
1751
|
+
```ts
|
|
1752
|
+
collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).splitIn(3).toArray()
|
|
1753
|
+
// [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
#### `sum()`
|
|
1757
|
+
|
|
1758
|
+
`sum()` returns the sum of the items, or of a key's values, or of what a function returns:
|
|
1759
|
+
|
|
1760
|
+
```ts
|
|
1761
|
+
collect([1, 2, 3, 4, 5]).sum()
|
|
1762
|
+
// 15
|
|
1763
|
+
|
|
1764
|
+
collect([
|
|
1765
|
+
{ name: 'JavaScript: The Good Parts', pages: 176 },
|
|
1766
|
+
{ name: 'JavaScript: The Definitive Guide', pages: 1096 },
|
|
1767
|
+
]).sum('pages')
|
|
1768
|
+
// 1272
|
|
1769
|
+
|
|
1770
|
+
collect([
|
|
1771
|
+
{ name: 'Chair', colors: ['Black'] },
|
|
1772
|
+
{ name: 'Desk', colors: ['Black', 'Mahogany'] },
|
|
1773
|
+
]).sum((product) => product.colors.length)
|
|
1774
|
+
// 3
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
Only numbers count: `collect(['1', 2]).sum()` is `2`, where Laravel adds the numeric string.
|
|
1778
|
+
|
|
1779
|
+
#### `take()`
|
|
1780
|
+
|
|
1781
|
+
`take()` returns the given number of items from the start, or from the end for a negative number:
|
|
1782
|
+
|
|
1783
|
+
```ts
|
|
1784
|
+
collect([0, 1, 2, 3, 4, 5]).take(3).all()
|
|
1785
|
+
// [0, 1, 2]
|
|
1786
|
+
|
|
1787
|
+
collect([0, 1, 2, 3, 4, 5]).take(-2).all()
|
|
1788
|
+
// [4, 5]
|
|
1789
|
+
```
|
|
1790
|
+
|
|
1791
|
+
#### `takeUntil()`
|
|
1792
|
+
|
|
1793
|
+
`takeUntil()` returns the items before the first one that passes the function, or is the value, compared strictly.
|
|
1794
|
+
When none does, it returns every item:
|
|
1795
|
+
|
|
1796
|
+
```ts
|
|
1797
|
+
collect([1, 2, 3, 4]).takeUntil((item) => item >= 3).all()
|
|
1798
|
+
// [1, 2]
|
|
1799
|
+
|
|
1800
|
+
collect([1, 2, 3, 4]).takeUntil(3).all()
|
|
1801
|
+
// [1, 2]
|
|
1802
|
+
```
|
|
1803
|
+
|
|
1804
|
+
#### `takeWhile()`
|
|
1805
|
+
|
|
1806
|
+
`takeWhile()` returns the items before the first one that fails the function, or isn't the value:
|
|
1807
|
+
|
|
1808
|
+
```ts
|
|
1809
|
+
collect([1, 2, 3, 4]).takeWhile((item) => item < 3).all()
|
|
1810
|
+
// [1, 2]
|
|
1811
|
+
```
|
|
1812
|
+
|
|
1813
|
+
#### `tap()`
|
|
1814
|
+
|
|
1815
|
+
`tap()` passes the collection to the function and returns the collection, so you can look at it in the middle of a
|
|
1816
|
+
chain:
|
|
1817
|
+
|
|
1818
|
+
```ts
|
|
1819
|
+
collect([2, 4, 3, 1, 5])
|
|
1820
|
+
.sort()
|
|
1821
|
+
.tap((collection) => console.log(collection.all()))
|
|
1822
|
+
.shift()
|
|
1823
|
+
// 1
|
|
1824
|
+
```
|
|
1825
|
+
|
|
1826
|
+
#### `times()`
|
|
1827
|
+
|
|
1828
|
+
Static. `Collection.times()` calls the function the given number of times, with the numbers from 1, and returns a
|
|
1829
|
+
collection of the results:
|
|
1830
|
+
|
|
1831
|
+
```ts
|
|
1832
|
+
Collection.times(10, (number) => number * 9).all()
|
|
1833
|
+
// [9, 18, 27, 36, 45, 54, 63, 72, 81, 90]
|
|
1834
|
+
```
|
|
1835
|
+
|
|
1836
|
+
#### `toArray()`
|
|
1837
|
+
|
|
1838
|
+
`toArray()` returns the items as an array, with each item that has a `toArray()` method, such as a model or a
|
|
1839
|
+
collection, turned into what it returns:
|
|
1840
|
+
|
|
1841
|
+
```ts
|
|
1842
|
+
collect([collect([1, 2]), collect([3])]).toArray()
|
|
1843
|
+
// [[1, 2], [3]]
|
|
1844
|
+
```
|
|
1845
|
+
|
|
1846
|
+
To get the items as they are, use [`all()`](#all).
|
|
1847
|
+
|
|
1848
|
+
#### `toJson()`
|
|
1849
|
+
|
|
1850
|
+
`toJson()` returns the items as JSON. `toPrettyJson()` indents it with four spaces, and `toJSON()` is what
|
|
1851
|
+
`JSON.stringify()` calls, so a collection inside an object is written as an array:
|
|
1852
|
+
|
|
1853
|
+
```ts
|
|
1854
|
+
collect([{ name: 'Desk', price: 200 }]).toJson()
|
|
1855
|
+
// '[{"name":"Desk","price":200}]'
|
|
1856
|
+
|
|
1857
|
+
JSON.stringify({ items: collect([1, 2]) })
|
|
1858
|
+
// '{"items":[1,2]}'
|
|
1859
|
+
```
|
|
1860
|
+
|
|
1861
|
+
#### `transform()`
|
|
1862
|
+
|
|
1863
|
+
`transform()` replaces each item with what the function returns, changing the collection:
|
|
1864
|
+
|
|
1865
|
+
```ts
|
|
1866
|
+
const collection = collect([1, 2, 3, 4, 5])
|
|
1867
|
+
|
|
1868
|
+
collection.transform((item) => item * 2)
|
|
1869
|
+
|
|
1870
|
+
collection.all()
|
|
1871
|
+
// [2, 4, 6, 8, 10]
|
|
1872
|
+
```
|
|
1873
|
+
|
|
1874
|
+
It returns the collection, typed with the new items. To keep the collection as it is, use [`map()`](#map).
|
|
1875
|
+
|
|
1876
|
+
#### `unique()`
|
|
1877
|
+
|
|
1878
|
+
`unique()` keeps the first of each value, compared strictly:
|
|
1879
|
+
|
|
1880
|
+
```ts
|
|
1881
|
+
collect([1, 1, 2, 2, 3, 4, 2]).unique().all()
|
|
1882
|
+
// [1, 2, 3, 4]
|
|
1883
|
+
```
|
|
1884
|
+
|
|
1885
|
+
Given a key or a function, it keeps the first item of each value it gives:
|
|
1886
|
+
|
|
1887
|
+
```ts
|
|
1888
|
+
const collection = collect([
|
|
1889
|
+
{ name: 'iPhone 6', brand: 'Apple', type: 'phone' },
|
|
1890
|
+
{ name: 'iPhone 5', brand: 'Apple', type: 'phone' },
|
|
1891
|
+
{ name: 'Apple Watch', brand: 'Apple', type: 'watch' },
|
|
1892
|
+
{ name: 'Galaxy S6', brand: 'Samsung', type: 'phone' },
|
|
1893
|
+
{ name: 'Galaxy Gear', brand: 'Samsung', type: 'watch' },
|
|
1894
|
+
])
|
|
1895
|
+
|
|
1896
|
+
collection.unique('brand').pluck('name').all()
|
|
1897
|
+
// ['iPhone 6', 'Galaxy S6']
|
|
1898
|
+
|
|
1899
|
+
collection.unique((item) => item.brand + item.type).pluck('name').all()
|
|
1900
|
+
// ['iPhone 6', 'Apple Watch', 'Galaxy S6', 'Galaxy Gear']
|
|
1901
|
+
```
|
|
1902
|
+
|
|
1903
|
+
Laravel's `unique()` compares loosely, so `1` and `'1'` are one value there and two here.
|
|
1904
|
+
|
|
1905
|
+
#### `uniqueStrict()`
|
|
1906
|
+
|
|
1907
|
+
The same as [`unique()`](#unique), which already compares strictly.
|
|
1908
|
+
|
|
1909
|
+
#### `unless()`
|
|
1910
|
+
|
|
1911
|
+
`unless()` calls the function unless the first argument is truthy, the opposite of [`when()`](#when):
|
|
1912
|
+
|
|
1913
|
+
```ts
|
|
1914
|
+
const collection = collect([1, 2, 3])
|
|
1915
|
+
|
|
1916
|
+
collection.unless(true, (collection) => collection.push(4))
|
|
1917
|
+
|
|
1918
|
+
collection.unless(false, (collection) => collection.push(5))
|
|
1919
|
+
|
|
1920
|
+
collection.all()
|
|
1921
|
+
// [1, 2, 3, 5]
|
|
1922
|
+
```
|
|
1923
|
+
|
|
1924
|
+
#### `unlessEmpty()`
|
|
1925
|
+
|
|
1926
|
+
Another name for [`whenNotEmpty()`](#whennotempty).
|
|
1927
|
+
|
|
1928
|
+
#### `unlessNotEmpty()`
|
|
1929
|
+
|
|
1930
|
+
Another name for [`whenEmpty()`](#whenempty).
|
|
1931
|
+
|
|
1932
|
+
#### `unshift()`
|
|
1933
|
+
|
|
1934
|
+
`unshift()` adds items at the start, in the order you give them, changing the collection:
|
|
1935
|
+
|
|
1936
|
+
```ts
|
|
1937
|
+
const collection = collect([3])
|
|
1938
|
+
|
|
1939
|
+
collection.unshift(1, 2)
|
|
1940
|
+
|
|
1941
|
+
collection.all()
|
|
1942
|
+
// [1, 2, 3]
|
|
1943
|
+
```
|
|
1944
|
+
|
|
1945
|
+
#### `unwrap()`
|
|
1946
|
+
|
|
1947
|
+
Static. `Collection.unwrap()` returns a collection's items as an array, and any other value as it is:
|
|
1948
|
+
|
|
1949
|
+
```ts
|
|
1950
|
+
Collection.unwrap(collect(['John Doe']))
|
|
1951
|
+
// ['John Doe']
|
|
1952
|
+
|
|
1953
|
+
Collection.unwrap('John Doe')
|
|
1954
|
+
// 'John Doe'
|
|
1955
|
+
```
|
|
1956
|
+
|
|
1957
|
+
#### `value()`
|
|
1958
|
+
|
|
1959
|
+
`value()` returns a key's value from the first item where it is truthy:
|
|
1960
|
+
|
|
1961
|
+
```ts
|
|
1962
|
+
const collection = collect([
|
|
1963
|
+
{ product: 'Desk', price: 200 },
|
|
1964
|
+
{ product: 'Speaker', price: 400 },
|
|
1965
|
+
])
|
|
1966
|
+
|
|
1967
|
+
collection.value('price')
|
|
1968
|
+
// 200
|
|
1969
|
+
```
|
|
1970
|
+
|
|
1971
|
+
A price of `0` would be skipped. When no item has a truthy value, it returns `undefined`, or the default you give.
|
|
1972
|
+
|
|
1973
|
+
#### `values()`
|
|
1974
|
+
|
|
1975
|
+
`values()` returns a copy of the collection. In Laravel it numbers the items from 0 again, which an Oriel collection
|
|
1976
|
+
always does.
|
|
1977
|
+
|
|
1978
|
+
#### `when()`
|
|
1979
|
+
|
|
1980
|
+
`when()` calls the function when the first argument is truthy. The function receives the collection and the value:
|
|
1981
|
+
|
|
1982
|
+
```ts
|
|
1983
|
+
const collection = collect([1, 2, 3])
|
|
1984
|
+
|
|
1985
|
+
collection.when(true, (collection, value) => collection.push(4))
|
|
1986
|
+
|
|
1987
|
+
collection.when(false, (collection, value) => collection.push(5))
|
|
1988
|
+
|
|
1989
|
+
collection.all()
|
|
1990
|
+
// [1, 2, 3, 4]
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
A third argument is a function to call when the value is falsy. A function as the first argument is called with the
|
|
1994
|
+
collection, and its result decides. `when()` returns what the function returns, or the collection when it returns
|
|
1995
|
+
nothing.
|
|
1996
|
+
|
|
1997
|
+
#### `whenEmpty()`
|
|
1998
|
+
|
|
1999
|
+
`whenEmpty()` calls the function when the collection is empty, and a second one, if you give it, when it isn't:
|
|
2000
|
+
|
|
2001
|
+
```ts
|
|
2002
|
+
const collection = collect<string>([])
|
|
2003
|
+
|
|
2004
|
+
collection.whenEmpty((collection) => collection.push('Adam'))
|
|
2005
|
+
|
|
2006
|
+
collection.all()
|
|
2007
|
+
// ['Adam']
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
#### `whenNotEmpty()`
|
|
2011
|
+
|
|
2012
|
+
`whenNotEmpty()` calls the function when the collection has items, and a second one, if you give it, when it is
|
|
2013
|
+
empty:
|
|
2014
|
+
|
|
2015
|
+
```ts
|
|
2016
|
+
const collection = collect(['Michael', 'Tom'])
|
|
2017
|
+
|
|
2018
|
+
collection.whenNotEmpty((collection) => collection.push('Adam'))
|
|
2019
|
+
|
|
2020
|
+
collection.all()
|
|
2021
|
+
// ['Michael', 'Tom', 'Adam']
|
|
2022
|
+
```
|
|
2023
|
+
|
|
2024
|
+
#### `where()`
|
|
2025
|
+
|
|
2026
|
+
`where()` keeps the items whose key has the value, compared loosely:
|
|
2027
|
+
|
|
2028
|
+
```ts
|
|
2029
|
+
const collection = collect([
|
|
2030
|
+
{ product: 'Desk', price: 200 },
|
|
2031
|
+
{ product: 'Chair', price: 100 },
|
|
2032
|
+
{ product: 'Bookcase', price: 150 },
|
|
2033
|
+
{ product: 'Door', price: 100 },
|
|
2034
|
+
])
|
|
2035
|
+
|
|
2036
|
+
collection.where('price', 100).all()
|
|
2037
|
+
// [{ product: 'Chair', price: 100 }, { product: 'Door', price: 100 }]
|
|
2038
|
+
```
|
|
2039
|
+
|
|
2040
|
+
An operator goes between the key and the value: `=`, `==`, `===`, `!=`, `!==`, `<>`, `<`, `<=`, `>` or `>=`:
|
|
2041
|
+
|
|
2042
|
+
```ts
|
|
2043
|
+
declare const collection: Collection<{ product: string; price: number }>
|
|
2044
|
+
// ---cut---
|
|
2045
|
+
collection.where('price', '>', 120).pluck('product').all()
|
|
2046
|
+
// ['Desk', 'Bookcase']
|
|
2047
|
+
```
|
|
2048
|
+
|
|
2049
|
+
With a key alone, it keeps the items whose key is truthy. The key may be a function of the item.
|
|
2050
|
+
|
|
2051
|
+
#### `whereBetween()`
|
|
2052
|
+
|
|
2053
|
+
`whereBetween()` keeps the items whose key is between two values, both included:
|
|
2054
|
+
|
|
2055
|
+
```ts
|
|
2056
|
+
const collection = collect([
|
|
2057
|
+
{ product: 'Desk', price: 200 },
|
|
2058
|
+
{ product: 'Chair', price: 80 },
|
|
2059
|
+
{ product: 'Bookcase', price: 150 },
|
|
2060
|
+
{ product: 'Pencil', price: 30 },
|
|
2061
|
+
{ product: 'Door', price: 100 },
|
|
2062
|
+
])
|
|
2063
|
+
|
|
2064
|
+
collection.whereBetween('price', [100, 200]).pluck('product').all()
|
|
2065
|
+
// ['Desk', 'Bookcase', 'Door']
|
|
2066
|
+
```
|
|
2067
|
+
|
|
2068
|
+
#### `whereIn()`
|
|
2069
|
+
|
|
2070
|
+
`whereIn()` keeps the items whose key is one of the values, compared strictly:
|
|
2071
|
+
|
|
2072
|
+
```ts
|
|
2073
|
+
const collection = collect([
|
|
2074
|
+
{ product: 'Desk', price: 200 },
|
|
2075
|
+
{ product: 'Chair', price: 100 },
|
|
2076
|
+
{ product: 'Bookcase', price: 150 },
|
|
2077
|
+
{ product: 'Door', price: 100 },
|
|
2078
|
+
])
|
|
2079
|
+
|
|
2080
|
+
collection.whereIn('price', [150, 200]).pluck('product').all()
|
|
2081
|
+
// ['Desk', 'Bookcase']
|
|
2082
|
+
```
|
|
2083
|
+
|
|
2084
|
+
Laravel's `whereIn()` compares loosely, and `whereInStrict()` strictly.
|
|
2085
|
+
|
|
2086
|
+
#### `whereInStrict()`
|
|
2087
|
+
|
|
2088
|
+
The same as [`whereIn()`](#wherein), which already compares strictly.
|
|
2089
|
+
|
|
2090
|
+
#### `whereInstanceOf()`
|
|
2091
|
+
|
|
2092
|
+
`whereInstanceOf()` keeps the items that are instances of a class, or of one of a list of classes, and types the
|
|
2093
|
+
result with them:
|
|
2094
|
+
|
|
2095
|
+
```ts
|
|
2096
|
+
declare const people: Collection<User | Post>
|
|
2097
|
+
// ---cut---
|
|
2098
|
+
const users = people.whereInstanceOf(User)
|
|
2099
|
+
```
|
|
2100
|
+
|
|
2101
|
+
#### `whereNotBetween()`
|
|
2102
|
+
|
|
2103
|
+
`whereNotBetween()` keeps the items whose key is outside two values:
|
|
2104
|
+
|
|
2105
|
+
```ts
|
|
2106
|
+
const collection = collect([
|
|
2107
|
+
{ product: 'Desk', price: 200 },
|
|
2108
|
+
{ product: 'Chair', price: 80 },
|
|
2109
|
+
{ product: 'Bookcase', price: 150 },
|
|
2110
|
+
{ product: 'Pencil', price: 30 },
|
|
2111
|
+
{ product: 'Door', price: 100 },
|
|
2112
|
+
])
|
|
2113
|
+
|
|
2114
|
+
collection.whereNotBetween('price', [100, 200]).pluck('product').all()
|
|
2115
|
+
// ['Chair', 'Pencil']
|
|
2116
|
+
```
|
|
2117
|
+
|
|
2118
|
+
#### `whereNotIn()`
|
|
2119
|
+
|
|
2120
|
+
`whereNotIn()` keeps the items whose key isn't one of the values, compared strictly:
|
|
2121
|
+
|
|
2122
|
+
```ts
|
|
2123
|
+
const collection = collect([
|
|
2124
|
+
{ product: 'Desk', price: 200 },
|
|
2125
|
+
{ product: 'Chair', price: 100 },
|
|
2126
|
+
{ product: 'Bookcase', price: 150 },
|
|
2127
|
+
{ product: 'Door', price: 100 },
|
|
2128
|
+
])
|
|
2129
|
+
|
|
2130
|
+
collection.whereNotIn('price', [150, 200]).pluck('product').all()
|
|
2131
|
+
// ['Chair', 'Door']
|
|
2132
|
+
```
|
|
2133
|
+
|
|
2134
|
+
#### `whereNotInStrict()`
|
|
2135
|
+
|
|
2136
|
+
The same as [`whereNotIn()`](#wherenotin), which already compares strictly.
|
|
2137
|
+
|
|
2138
|
+
#### `whereNotNull()`
|
|
2139
|
+
|
|
2140
|
+
`whereNotNull()` keeps the items whose key isn't `null` or `undefined`, or, with no key, the items that aren't:
|
|
2141
|
+
|
|
2142
|
+
```ts
|
|
2143
|
+
const collection = collect([{ name: 'Desk' }, { name: null }, { name: 'Bookcase' }])
|
|
2144
|
+
|
|
2145
|
+
collection.whereNotNull('name').all()
|
|
2146
|
+
// [{ name: 'Desk' }, { name: 'Bookcase' }]
|
|
2147
|
+
```
|
|
2148
|
+
|
|
2149
|
+
#### `whereNull()`
|
|
2150
|
+
|
|
2151
|
+
`whereNull()` keeps the items whose key is `null` or `undefined`, an item without the key included, or, with no key,
|
|
2152
|
+
the items that are:
|
|
2153
|
+
|
|
2154
|
+
```ts
|
|
2155
|
+
const collection = collect([{ name: 'Desk' }, { name: null }, { name: 'Bookcase' }])
|
|
2156
|
+
|
|
2157
|
+
collection.whereNull('name').all()
|
|
2158
|
+
// [{ name: null }]
|
|
2159
|
+
```
|
|
2160
|
+
|
|
2161
|
+
#### `whereStrict()`
|
|
2162
|
+
|
|
2163
|
+
`whereStrict()` is [`where()`](#where) with a key and a value, compared strictly:
|
|
2164
|
+
|
|
2165
|
+
```ts
|
|
2166
|
+
collect([{ price: 100 }]).whereStrict('price', '100').count()
|
|
2167
|
+
// 0
|
|
2168
|
+
```
|
|
2169
|
+
|
|
2170
|
+
#### `wrap()`
|
|
2171
|
+
|
|
2172
|
+
Static. `Collection.wrap()` makes a collection of a value: an array's or a collection's items, nothing for `null` or
|
|
2173
|
+
`undefined`, and any other value as the one item:
|
|
2174
|
+
|
|
2175
|
+
```ts
|
|
2176
|
+
Collection.wrap('John Doe').all()
|
|
2177
|
+
// ['John Doe']
|
|
2178
|
+
|
|
2179
|
+
Collection.wrap(['John Doe']).all()
|
|
2180
|
+
// ['John Doe']
|
|
2181
|
+
|
|
2182
|
+
Collection.wrap(null).all()
|
|
2183
|
+
// []
|
|
2184
|
+
```
|
|
2185
|
+
|
|
2186
|
+
#### `zip()`
|
|
2187
|
+
|
|
2188
|
+
`zip()` pairs each item with the item at the same index of the lists you give. A shorter list gives `undefined`:
|
|
2189
|
+
|
|
2190
|
+
```ts
|
|
2191
|
+
const zipped = collect(['Chair', 'Desk']).zip([100, 200])
|
|
2192
|
+
|
|
2193
|
+
zipped.toArray()
|
|
2194
|
+
// [['Chair', 100], ['Desk', 200]]
|
|
2195
|
+
```
|
|
2196
|
+
|
|
2197
|
+
## Higher order messages
|
|
2198
|
+
|
|
2199
|
+
Laravel's higher order messages, such as `$users->each->markAsVip()` and `$users->sum->votes`, aren't in Oriel.
|
|
2200
|
+
Pass a function or a key: `users.each((user) => user.markAsVip())`, `users.sum('votes')`.
|
|
2201
|
+
|
|
2202
|
+
## Lazy collections
|
|
2203
|
+
|
|
2204
|
+
### Introduction
|
|
2205
|
+
|
|
2206
|
+
A lazy collection reads its items one at a time, as you ask for them, so a large query or file never sits in memory
|
|
2207
|
+
all at once. Its items can come from an async source, such as a database cursor, so it is async: you go through it
|
|
2208
|
+
with `for await`, and its methods that return a value return a promise.
|
|
2209
|
+
|
|
2210
|
+
Your queries return one with `lazy()`, which reads the rows a chunk at a time, 1,000 by default:
|
|
2211
|
+
|
|
2212
|
+
```ts
|
|
2213
|
+
for await (const user of User.lazy()) {
|
|
2214
|
+
// ...
|
|
2215
|
+
}
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
### Creating lazy collections
|
|
2219
|
+
|
|
2220
|
+
`LazyCollection.make()` takes a function that returns an iterable, or an async one. A generator function is the
|
|
2221
|
+
usual choice:
|
|
2222
|
+
|
|
2223
|
+
```ts title="app/Support/logLines.ts"
|
|
2224
|
+
import { createReadStream } from 'node:fs'
|
|
2225
|
+
import { createInterface } from 'node:readline'
|
|
2226
|
+
import { LazyCollection } from '@orieljs/framework'
|
|
2227
|
+
|
|
2228
|
+
export function logLines(path: string): LazyCollection<string> {
|
|
2229
|
+
return LazyCollection.make(() => createInterface({ input: createReadStream(path) }))
|
|
2230
|
+
}
|
|
2231
|
+
```
|
|
2232
|
+
|
|
2233
|
+
```ts
|
|
2234
|
+
declare function logLines(path: string): LazyCollection<string>
|
|
2235
|
+
// ---cut---
|
|
2236
|
+
const errors = await logLines('storage/logs/oriel.log')
|
|
2237
|
+
.filter((line) => line.includes('"level":50'))
|
|
2238
|
+
.take(10)
|
|
2239
|
+
.collect()
|
|
2240
|
+
```
|
|
2241
|
+
|
|
2242
|
+
`collection.lazy()` makes one from a collection.
|
|
2243
|
+
|
|
2244
|
+
### Lazy collection methods
|
|
2245
|
+
|
|
2246
|
+
A lazy collection has these methods. The first four return a new lazy collection, which reads nothing until you go
|
|
2247
|
+
through it; the others go through the items, and return a promise:
|
|
2248
|
+
|
|
2249
|
+
- `map(callback)`: each item passed through the callback, which may be async;
|
|
2250
|
+
- `filter(callback)`: the items the callback passes;
|
|
2251
|
+
- `take(count)`: the first items, and stops reading after them;
|
|
2252
|
+
- `chunk(size)`: collections of `size` items;
|
|
2253
|
+
- `each(callback)`: calls the callback with each item and its index; returning `false` stops;
|
|
2254
|
+
- `first(callback?)`: the first item, or the first that passes the callback, or `undefined`;
|
|
2255
|
+
- `count()`: the number of items;
|
|
2256
|
+
- `collect()`: every item, in a `Collection`.
|
|
2257
|
+
|
|
2258
|
+
```ts
|
|
2259
|
+
declare function logLines(path: string): LazyCollection<string>
|
|
2260
|
+
// ---cut---
|
|
2261
|
+
const count = await logLines('storage/logs/oriel.log')
|
|
2262
|
+
.map((line) => JSON.parse(line) as { level: number })
|
|
2263
|
+
.filter((entry) => entry.level >= 50)
|
|
2264
|
+
.count()
|
|
2265
|
+
```
|
|
2266
|
+
|
|
2267
|
+
> [!NOTE]
|
|
2268
|
+
> Laravel's lazy collections are synchronous, built on PHP generators, and have every method of a collection, and a
|
|
2269
|
+
> few of their own, such as `takeUntilTimeout()` and `remember()`. Oriel's are async, so they can read a database
|
|
2270
|
+
> cursor or a stream, and have the methods above: for any other, `collect()` the items you need first.
|