1 | # scour.js
|
2 |
|
3 |
|
4 |
|
5 | > Traverse objects and arrays immutably
|
6 |
|
7 | Scour is a general-purpose library for dealing with JSON trees.<br>
|
8 | As a simple utility with a broad purpose, it can be used to solve many problems. Use it to:
|
9 |
|
10 | - Manage your [Redux] datastore.
|
11 | - Provide a model layer to access data in your single-page app. [→](#models)
|
12 | - Navigate a large JSON tree easily.
|
13 | - Rejoice in having a lightweight alternative to [Immutable.js]. ([Compare](docs/comparison.md))
|
14 |
|
15 | [![Status](https://travis-ci.org/rstacruz/scour.svg?branch=master)](https://travis-ci.org/rstacruz/scour "See test builds")
|
16 |
|
17 | ## Install
|
18 |
|
19 | ```sh
|
20 | npm install --save-exact scourjs
|
21 | ```
|
22 |
|
23 | ```js
|
24 | window.scour // non commonjs
|
25 | const scour = require('scourjs') // commonjs/node
|
26 | import scour from 'scourjs' // es6 modules
|
27 | ```
|
28 |
|
29 | ## Features
|
30 |
|
31 | Calling `scour(object)` returns a wrapper that you can use to traverse `object`.
|
32 | Use [get()](#get) to retrieve values.
|
33 |
|
34 | ```js
|
35 | data =
|
36 | { users:
|
37 | { 1: { name: 'john' },
|
38 | 2: { name: 'shane', confirmed: true },
|
39 | 3: { name: 'barry', confirmed: true } } }
|
40 | ```
|
41 |
|
42 | ```js
|
43 | scour(data).get('users', '1', 'name') // => 'john'
|
44 | ```
|
45 |
|
46 | <br>
|
47 |
|
48 | ### Traversal
|
49 | Use [go()](#go) to dig into the structure. It will return another `scour`
|
50 | wrapper scoped to that object.
|
51 |
|
52 | ```js
|
53 | data =
|
54 | { users:
|
55 | { admins:
|
56 | { bob: { logged_in: true },
|
57 | sue: { logged_in: false } } } }
|
58 | ```
|
59 |
|
60 | ```js
|
61 | users = scour(data).go('users') // => [scour (admins)]
|
62 | admins = scour(data).go('users', 'admins') // => [scour (bob, sue)]
|
63 |
|
64 | admins.go('bob').get('logged_in') // => true
|
65 | ```
|
66 |
|
67 | <br>
|
68 |
|
69 | ### Chaining
|
70 |
|
71 | `scour()` provides a wrapper that can be used to chain methods. This is inspired by [Underscore] and [Lodash].
|
72 |
|
73 | ```js
|
74 | scour(data)
|
75 | .go('users')
|
76 | .filter({ admin: true })
|
77 | .value
|
78 | ```
|
79 |
|
80 | [Underscore]: http://underscorejs.org/
|
81 | [Lodash]: http://lodash.com/
|
82 |
|
83 | <br>
|
84 |
|
85 | ### Immutable modifications
|
86 |
|
87 | Use [set()](#set) to update values. Scout treats all data as immutable, so this
|
88 | doesn't modify your original `data`, but gets you a new one with the
|
89 | modifications made.
|
90 |
|
91 | ```js
|
92 | data = scour(data)
|
93 | .set(['users', '1', 'updated_at'], +new Date())
|
94 | .value
|
95 |
|
96 | // => { users:
|
97 | // { 1: { name: 'john', updated_at: 1450667171188 },
|
98 | // 2: { name: 'shane', confirmed: true },
|
99 | // 3: { name: 'barry', confirmed: true } } }
|
100 | ```
|
101 |
|
102 | <br>
|
103 |
|
104 | ### Advanced traversing
|
105 |
|
106 | Use [filter()] to filter results with advanced querying.
|
107 |
|
108 | ```js
|
109 | users = scour(data).go('users')
|
110 |
|
111 | users
|
112 | .filter({ confirmed: true })
|
113 | .at(0)
|
114 | .get('name') // => 'shane'
|
115 | ```
|
116 |
|
117 | <br>
|
118 |
|
119 | ### Models
|
120 |
|
121 | Use [use()](#use) to add your own methods to certain keypaths. This makes them behave like models.<br>
|
122 | See [a detailed example](docs/extensions_example.md) to learn more.
|
123 |
|
124 | ##### Sample data
|
125 |
|
126 |
|
127 |
|
128 | ```js
|
129 | data =
|
130 | { artists:
|
131 | { 1: { first_name: 'Louie', last_name: 'Armstrong' },
|
132 | 2: { first_name: 'Miles', last_name: 'Davis' } } }
|
133 | ```
|
134 |
|
135 | ##### Your models
|
136 |
|
137 |
|
138 |
|
139 | ```js
|
140 | Root = {
|
141 | artists () { return this.go('artists') }
|
142 | }
|
143 |
|
144 | Artist = {
|
145 | fullname () {
|
146 | return this.get('first_name') + ' ' + this.get('last_name')
|
147 | }
|
148 | }
|
149 | ```
|
150 |
|
151 | ##### Using with scour
|
152 |
|
153 |
|
154 |
|
155 | ```js
|
156 | db = scour(data)
|
157 | .use({
|
158 | '': Root,
|
159 | 'artists.*': Artist
|
160 | })
|
161 |
|
162 | db.artists().find({ name: 'Miles' }).fullname()
|
163 | //=> 'Miles Davis'
|
164 | ```
|
165 |
|
166 | <br>
|
167 |
|
168 | ## API
|
169 |
|
170 |
|
171 |
|
172 | ### scour
|
173 |
|
174 | > `scour(object)`
|
175 |
|
176 | Returns a scour instance wrapping `object`.
|
177 |
|
178 | ```js
|
179 | scour(obj)
|
180 | ```
|
181 |
|
182 | It can be called on any Object or Array. (In fact, it can be called on
|
183 | anything, but is only generally useful for Objects and Arrays.)
|
184 |
|
185 | ```js
|
186 | data = { menu: { visible: true, position: 'left' } }
|
187 | scour(data).get('menu.visible')
|
188 |
|
189 | list = [ { id: 2 }, { id: 5 }, { id: 12 } ]
|
190 | scour(list).get('0.id')
|
191 | ```
|
192 |
|
193 | __Chaining__:
|
194 | You can use it to start method chains. In fact, the intended use is to keep
|
195 | your root [scour] object around, and chain from this.
|
196 |
|
197 | ```js
|
198 | db = scour({ menu: { visible: true, position: 'left' } })
|
199 |
|
200 | // Elsewhere:
|
201 | menu = db.go('menu')
|
202 | menu.get('visible')
|
203 | ```
|
204 |
|
205 | __Properties__:
|
206 | It the [root], [value] and [keypath] properties.
|
207 |
|
208 | ```js
|
209 | s = scour(obj)
|
210 | s.root // => [scour object]
|
211 | s.value // => raw data (that is, `obj`)
|
212 | s.keypath // => string array
|
213 | ```
|
214 |
|
215 | __Accessing the value:__
|
216 | You can access the raw data using [value].
|
217 |
|
218 | ```js
|
219 | db = scour(data)
|
220 | db.value // => same as `data`
|
221 | db.go('users').value // => same as `data.users`
|
222 | ```
|
223 |
|
224 | ## Chaining methods
|
225 |
|
226 | These methods are used to traverse nested structures. All these
|
227 | methods return [scour] instances, making them suitable for chaining.
|
228 |
|
229 | #### On null values
|
230 | Note that `undefined`, `false` and `null` values are still [scour]-wrapped
|
231 | when returned from [go()], [at()] and [find()].
|
232 |
|
233 | ```js
|
234 | list = [ { name: 'Homer' }, { name: 'Bart' } ]
|
235 |
|
236 | scour(list).at(4) // => [ scour undefined ]
|
237 | scour(list).at(4).value // => undefined
|
238 | ```
|
239 |
|
240 | This is done so that you can chain methods safely even when something is null.
|
241 | This behavior is consistent with what you'd expect with jQuery.
|
242 |
|
243 | ```js
|
244 | data = { users: { ... } }
|
245 | db = scour(data)
|
246 |
|
247 | db.go('blogposts').map((post) => post.get('title'))
|
248 | // => []
|
249 | ```
|
250 |
|
251 | ### go
|
252 |
|
253 | > `go(keypath...)`
|
254 |
|
255 | Navigates down to a given `keypath`. Always returns a [scour] instance.
|
256 | Rules [on null values] apply.
|
257 |
|
258 | ```js
|
259 | data =
|
260 | { users:
|
261 | { 12: { name: 'steve', last: 'jobs' },
|
262 | 23: { name: 'bill', last: 'gates' } } }
|
263 |
|
264 | scour(data).go('users') // => [scour (users)]
|
265 | scour(data).go('users', '12') // => [scour (name, last)]
|
266 | scour(data).go('users', '12').get('name') // => 'steve'
|
267 | ```
|
268 |
|
269 | __Dot notation:__
|
270 | Keypaths can be given in dot notation or as an array. These statements are
|
271 | equivalent.
|
272 |
|
273 | ```js
|
274 | scour(data).go('users.12')
|
275 | scour(data).go('users', '12')
|
276 | scour(data).go(['users', '12'])
|
277 | ```
|
278 |
|
279 | __Non-objects:__
|
280 | If you use it on a non-object or non-array value, it will still be
|
281 | returned as a [scour] instance. This is not likely what you want; use
|
282 | [get()] instead.
|
283 |
|
284 | ```js
|
285 | attr = scour(data).go('users', '12', 'name')
|
286 | attr // => [scour object]
|
287 | attr.value // => 'steve'
|
288 | attr.keypath // => ['users', '12', 'name']
|
289 | ```
|
290 |
|
291 | ### at
|
292 |
|
293 | > `at(index)`
|
294 |
|
295 | Returns the item at `index`. This differs from `go` as this searches by
|
296 | index, not by key. This returns a the raw value, unlike [getAt()]. Rules
|
297 | [on null values] apply.
|
298 |
|
299 | ```js
|
300 | users =
|
301 | { 12: { name: 'steve' },
|
302 | 23: { name: 'bill' } }
|
303 |
|
304 | scour(users).at(0) // => [scour { name: 'steve' }]
|
305 | scour(users).get(12) // => [scour { name: 'steve' }]
|
306 | ```
|
307 |
|
308 | ### getAt
|
309 |
|
310 | > `getAt(index)`
|
311 |
|
312 | Returns the item at `index`. This differs from `get` as this searches by
|
313 | index, not by key. This returns a the raw value, unlike [at()].
|
314 |
|
315 | ```js
|
316 | users =
|
317 | { 12: { name: 'steve' },
|
318 | 23: { name: 'bill' } }
|
319 |
|
320 | scour(users).at(0) // => [scour { name: 'steve' }]
|
321 | scour(users).getAt(0) // => { name: 'steve' }
|
322 | ```
|
323 |
|
324 | ### filter
|
325 |
|
326 | > `filter(conditions)`
|
327 |
|
328 | Sifts through the values and returns a set that matches given
|
329 | `conditions`. Supports simple objects, MongoDB-style
|
330 | queries, and functions.
|
331 |
|
332 | ```js
|
333 | scour(data).filter({ name: 'Moe' })
|
334 | scour(data).filter({ name: { $in: ['Larry', 'Curly'] })
|
335 | scour(data).filter((item) => item.get('name') === 'Moe')
|
336 | ```
|
337 |
|
338 | __Filter by object:__
|
339 | If you pass an object as a condition, `filter()` will check if that object
|
340 | coincides with the objects in the collection.
|
341 |
|
342 | ```js
|
343 | scour(data).filter({ name: 'Moe' })
|
344 | ```
|
345 |
|
346 | __Filter by function:__
|
347 | You may pass a function as a parameter. In this case, the `item` being
|
348 | passed to the callback will be a [scour]-wrapped object. The result
|
349 | will also be a [scour]-wrapped object, making it chainable.
|
350 |
|
351 | ```js
|
352 | scour(data)
|
353 | .filter((item, key) => +item.get('price') > 200)
|
354 | .sortBy('price')
|
355 | .first()
|
356 | ```
|
357 |
|
358 | __Advanced queries:__
|
359 | MongoDB-style queries are supported as provided by [sift.js]. For
|
360 | reference, see [MongoDB Query Operators][query-ops].
|
361 |
|
362 | ```js
|
363 | scour(products).filter({ price: { $gt: 200 })
|
364 | scour(articles).filter({ published_at: { $not: null }})
|
365 | ```
|
366 |
|
367 | __Arrays or objects:__
|
368 | Both arrays and array-like objects are supported. In this example below,
|
369 | an object will be used as the input.
|
370 |
|
371 | ```js
|
372 | devices =
|
373 | { 1: { id: 1, name: 'Phone', mobile: true },
|
374 | 2: { id: 2, name: 'Tablet', mobile: true },
|
375 | 3: { id: 3, name: 'Desktop', mobile: false } }
|
376 |
|
377 | scour(devices).filter({ mobile: true }).len()
|
378 | // => 2
|
379 | ```
|
380 |
|
381 | Also see [scour.filter()] for the unwrapped version.
|
382 |
|
383 | [query-ops]: https://docs.mongodb.org/manual/reference/operator/query/
|
384 |
|
385 | ### reject
|
386 |
|
387 | > `reject(conditions)`
|
388 |
|
389 | Inverse of [filter()] -- see `filter()` documentation for details.
|
390 |
|
391 | ### find
|
392 |
|
393 | > `find(conditions)`
|
394 |
|
395 | Returns the first value that matches `conditions`. Supports MongoDB-style
|
396 | queries. For reference, see [MongoDB Query Operators][query-ops]. Also
|
397 | see [filter()], as this is functionally-equivalent to the first result of
|
398 | `filter()`. Rules [on null values] apply.
|
399 |
|
400 | [query-ops]: https://docs.mongodb.org/manual/reference/operator/query/
|
401 |
|
402 | ```js
|
403 | scour(data).find({ name: 'john' })
|
404 | scour(data).find({ name: { $in: ['moe', 'larry'] })
|
405 | ```
|
406 |
|
407 | ### first
|
408 |
|
409 | > `first()`
|
410 |
|
411 | Returns the first result as a [scour]-wrapped object. This is equivalent
|
412 | to [at(0)](#at).
|
413 |
|
414 | ### last
|
415 |
|
416 | > `last()`
|
417 |
|
418 | Returns the first result as a [scour]-wrapped object. This is equivalent
|
419 | to `at(len() - 1)`: see [at()] and [len()].
|
420 |
|
421 | ### sortBy
|
422 |
|
423 | > `sortBy(condition)`
|
424 |
|
425 | Sorts a collection. Returns a [scour]-wrapped object suitable for
|
426 | chaining. Like other chainable methods, this works on arrays as well as
|
427 | objects.
|
428 |
|
429 | ```js
|
430 | data =
|
431 | { 0: { name: 'Wilma' },
|
432 | 1: { name: 'Barney' },
|
433 | 2: { name: 'Fred' } }
|
434 |
|
435 | scour(data).sortBy('name').value
|
436 | // { 1: { name: 'Barney' },
|
437 | // 2: { name: 'Fred' },
|
438 | // 0: { name: 'Wilma' } }
|
439 | ```
|
440 |
|
441 | __Conditions:__
|
442 | The given condition can be a string or a function. When it's given as a
|
443 | function, the `item` being passed is a [scour]-wrapped object, just like
|
444 | in [forEach()] (et al). These two examples below are
|
445 | functionally-equivalent.
|
446 |
|
447 | ```js
|
448 | scour(data).sortBy('name')
|
449 | scour(data).sortBy((item) => item.get('name'))
|
450 | ```
|
451 |
|
452 | You may also define nested keys in dot-notation:
|
453 |
|
454 | ```js
|
455 | scour(data).sortBy('user.name')
|
456 | ```
|
457 |
|
458 | ## Reading methods
|
459 |
|
460 | For retrieving data.
|
461 |
|
462 | ### get
|
463 |
|
464 | > `get(keypath...)`
|
465 |
|
466 | Returns data in a given `keypath`.
|
467 |
|
468 | ```js
|
469 | data =
|
470 | { users:
|
471 | { 12: { name: 'steve' },
|
472 | 23: { name: 'bill' } } }
|
473 |
|
474 | scour(data).get('users') // => same as data.users
|
475 | scour(data).go('users').value // => same as data.users
|
476 | ```
|
477 |
|
478 | __Dot notation:__
|
479 | Like [go()], the `keypath` can be given in dot notation.
|
480 |
|
481 | ```js
|
482 | scour(data).get('books.featured.name')
|
483 | scour(data).get('books', 'featured', 'name')
|
484 | ```
|
485 |
|
486 | ### len
|
487 |
|
488 | > `len()`
|
489 |
|
490 | Returns the length of the object or array. For objects, it returns the
|
491 | number of keys.
|
492 |
|
493 | ```js
|
494 | users =
|
495 | { 12: { name: 'steve' },
|
496 | 23: { name: 'bill' } }
|
497 |
|
498 | names = scour(users).len() // => 2
|
499 | ```
|
500 |
|
501 | ### toArray
|
502 |
|
503 | > `toArray()`
|
504 |
|
505 | Returns an array. If the the value is an object, it returns the values of
|
506 | that object. If the value is an array, it returns it as is. Also aliased
|
507 | as `values()`.
|
508 |
|
509 | ```js
|
510 | users =
|
511 | { 12: { name: 'steve' },
|
512 | 23: { name: 'bill' } }
|
513 |
|
514 | names = scour(users).toArray()
|
515 | // => [ {name: 'steve'}, {name: 'bill'} ]
|
516 | ```
|
517 |
|
518 | ### keys
|
519 |
|
520 | > `keys()`
|
521 |
|
522 | Returns keys. If the value is an array, this returns the array's indices.
|
523 | Also see [toArray()] to retrieve the values instead.
|
524 |
|
525 | ## Writing methods
|
526 |
|
527 | These are methods for modifying an object/array tree immutably.
|
528 | Note that all these functions are immutable--it will not modify existing
|
529 | data, but rather spawn new objects with the modifications done on them.
|
530 |
|
531 | ### set
|
532 |
|
533 | > `set(keypath, value)`
|
534 |
|
535 | Sets values immutably. Returns a copy of the same object ([scour]-wrapped)
|
536 | with the modifications applied.
|
537 |
|
538 | ```js
|
539 | data = { bob: { name: 'Bob' } }
|
540 | db = scour(data)
|
541 | db.set([ 'bob', 'name' ], 'Robert')
|
542 | // db.value == { bob: { name: 'Robert' } }
|
543 | ```
|
544 |
|
545 | __Immutability:__
|
546 | This is an immutable function, and will return a new object. It won't
|
547 | modify your original object.
|
548 |
|
549 | ```js
|
550 | profile = scour({ name: 'John' })
|
551 | profile2 = profile.set('email', 'john@gmail.com')
|
552 |
|
553 | profile.value // => { name: 'John' }
|
554 | profile2.value // => { name: 'John', email: 'john@gmail.com' }
|
555 | ```
|
556 |
|
557 | __Using within a scope:__
|
558 | Be aware that using all writing methods ([set()], [del()], [extend()]) on
|
559 | scoped objects (ie, made with [go()]) will spawn a new [root] object. If
|
560 | you're keeping a reference to the root object, you'll need to update it
|
561 | accordingly.
|
562 |
|
563 | ```js
|
564 | db = scour(data)
|
565 | book = db.go('book')
|
566 | book.root === db // correct so far
|
567 |
|
568 | book = book.set('title', 'IQ84')
|
569 | book = book.del('sale_price')
|
570 | book.root !== db // `root` has been updated
|
571 | ```
|
572 |
|
573 | __Dot notation:__
|
574 | Like [go()] and [get()], the keypath can be given in dot notation or an
|
575 | array.
|
576 |
|
577 | ```js
|
578 | scour(data).set('menu.left.visible', true)
|
579 | scour(data).set(['menu', 'left', 'visible'], true)
|
580 | ```
|
581 |
|
582 | ### del
|
583 |
|
584 | > `del(keypath)`
|
585 |
|
586 | Deletes values immutably. Returns a copy of the same object
|
587 | ([scour]-wrapped) with the modifications applied.
|
588 |
|
589 | Like [set()], the keypath can be given in dot notation or an
|
590 | array.
|
591 |
|
592 | ```js
|
593 | scour(data).del('menu.left.visible')
|
594 | scour(data).del(['menu', 'left', 'visible'])
|
595 | ```
|
596 |
|
597 | See [set()] for more information on working with immutables.
|
598 |
|
599 | ### extend
|
600 |
|
601 | > `extend(objects...)`
|
602 |
|
603 | Extends the data with more values. Returns a [scour]-wrapped object. Just
|
604 | like [Object.assign], you may pass multiple objects to the parameters.
|
605 |
|
606 | ```js
|
607 | data = { a: 1, b: 2 }
|
608 | data2 = scour(data).extend({ c: 3 })
|
609 | ```
|
610 |
|
611 | ```js
|
612 | data2 // => [scour { a: 1, b: 2, c: 3 }]
|
613 | data2.value // => { a: 1, b: 2, c: 3 }
|
614 | ```
|
615 |
|
616 | When used with anything non-object, it will be overridden.
|
617 |
|
618 | ```js
|
619 | data = {}
|
620 | db = scour(data)
|
621 | db = db.go('state').extend({ pressed: true }).root
|
622 |
|
623 | db.value // => { state: { pressed: true } }
|
624 | ```
|
625 |
|
626 | See [set()] for more information on working with immutables.
|
627 |
|
628 | ## Utility methods
|
629 |
|
630 | For stuff.
|
631 |
|
632 | ### use
|
633 |
|
634 | > `use(extensions)`
|
635 |
|
636 | Extends functionality for certain keypaths with custom methods.
|
637 | See [Extensions example] for examples.
|
638 |
|
639 | ```js
|
640 | data =
|
641 | { users:
|
642 | { 12: { name: 'steve', surname: 'jobs' },
|
643 | 23: { name: 'bill', surname: 'gates' } } }
|
644 |
|
645 | extensions = {
|
646 | 'users.*': {
|
647 | fullname () {
|
648 | return this.get('name') + ' ' + this.get('surname')
|
649 | }
|
650 | }
|
651 | }
|
652 |
|
653 | scour(data)
|
654 | .use(extensions)
|
655 | .get('users', 12)
|
656 | .fullname() // => 'bill gates'
|
657 | ```
|
658 |
|
659 | __Extensions format:__
|
660 | The parameter `extension` is an object, with keys being keypath globs, and
|
661 | values being properties to be extended.
|
662 |
|
663 | ```js
|
664 | .use({
|
665 | 'books.*': { ... },
|
666 | 'authors.*': { ... },
|
667 | 'publishers.*': { ... }
|
668 | })
|
669 | ```
|
670 |
|
671 | __Extending root:__
|
672 | To bind properties to the root method, use an empty string as the keypath.
|
673 |
|
674 | ```js
|
675 | .use({
|
676 | '': {
|
677 | users() { return this.go('users') },
|
678 | authors() { return this.go('authors') }
|
679 | }
|
680 | })
|
681 | ```
|
682 |
|
683 | __Keypath filtering:__
|
684 | You can use glob-like `*` and `**` to match parts of a keypath. A `*` will
|
685 | match any one segment, and `**` will match one or many segments. Here are
|
686 | some examples:
|
687 |
|
688 | - `users.*` - will match `users.1`, but not `users.1.photos`
|
689 | - `users.**` - will match `users.1.photos`
|
690 | - `users.*.photos` - will match `users.1.photos`
|
691 | - `**` will match anything
|
692 |
|
693 | __When using outside root:__
|
694 | Any extensions in a scoped object (ie, made with [go()]) will be used relative
|
695 | to it. For instance, if you define an extension to `admins.*` inside
|
696 | `.go('users')`, it will affect `users.
|
697 |
|
698 | ```js
|
699 | data = { users: { john: { } }
|
700 | db = scour(data)
|
701 |
|
702 | users = db.go('users')
|
703 | .use({ '*': { hasName () { return !!this.get('name') } })
|
704 |
|
705 | users.go('john').hasName() // works
|
706 | ```
|
707 |
|
708 | While this is supported, it is *not* recommended: these extensions will not
|
709 | propagate back to the root, and any objects taken from the root will not
|
710 | have those extensions applied to them.
|
711 |
|
712 | ```js
|
713 | users.go('john').hasName() // works
|
714 | db.go('users.john').hasName() // doesn't work
|
715 | ```
|
716 |
|
717 | ### toJSON
|
718 |
|
719 | > `toJSON()`
|
720 |
|
721 | Returns the value for serialization. This allows `JSON.stringify()` to
|
722 | work with `scour`-wrapped objects. The name of this method is a bit
|
723 | confusing, as it doesn't actually return a JSON string — but I'm afraid
|
724 | that it's the way that the JavaScript API for [JSON.stringify] works.
|
725 |
|
726 | [JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior
|
727 |
|
728 | ### equal
|
729 |
|
730 | > `equal(other)`
|
731 |
|
732 | Checks for equality between two Scour-wrapped objects.
|
733 |
|
734 | ```js
|
735 | a = scour(data)
|
736 | b = scour(data)
|
737 |
|
738 | a.equal(b) // => true
|
739 | ```
|
740 |
|
741 | ## Iteration methods
|
742 |
|
743 | These methods are generally useful for collections. These
|
744 | methods can work with either arrays or array-like objects, such as
|
745 | below.
|
746 |
|
747 | ```js
|
748 | subjects =
|
749 | { 1: { id: 1, title: 'Math', level: 101 },
|
750 | 2: { id: 2, title: 'Science', level: 103 },
|
751 | 3: { id: 3, title: 'History', level: 102 } }
|
752 | ```
|
753 |
|
754 | __Values:__
|
755 | For all these functions, The items passed onto the callbacks _is_ a
|
756 | [scour]-wrapped object. Use `item.value` or `this` to access the raw
|
757 | values.
|
758 |
|
759 | ```js
|
760 | scour(subjects).forEach((subject, key) => {
|
761 | console.log(subject.get('title'))
|
762 | })
|
763 | ```
|
764 |
|
765 | __Return values:__
|
766 | For methods that return values (such as [map()], the returned results _is
|
767 | not_ a [scour]-wrapped object, and isn't suitable for chaining.
|
768 |
|
769 | ```js
|
770 | scour(subjects).map((subject, key) => {
|
771 | return subject.get('title') + ' ' + subject.get('level')
|
772 | })
|
773 | // => [ 'Math 101', 'Science 103', 'History 102' ]
|
774 | ```
|
775 |
|
776 | ### forEach
|
777 |
|
778 | > `forEach(function(item, key, index))`
|
779 |
|
780 | Loops through each item. Supports both arrays and objects.
|
781 | The rules specified in [Iteration methods] apply.
|
782 |
|
783 | ```js
|
784 | users =
|
785 | { 12: { name: 'steve' },
|
786 | 23: { name: 'bill' } }
|
787 |
|
788 | scour(users).each((user, key) => {
|
789 | console.log(user.get('name'))
|
790 | })
|
791 | ```
|
792 |
|
793 | The values passed onto the function are:
|
794 |
|
795 | - `item` - the value; always a scour object.
|
796 | - `key` - the key.
|
797 | - `index` - the index.
|
798 |
|
799 | ### each
|
800 |
|
801 | > `each(fn)`
|
802 |
|
803 | Alias for [forEach](#foreach).
|
804 |
|
805 | ### map
|
806 |
|
807 | > `map(function(item, key))`
|
808 |
|
809 | Loops through each item and returns an array based on the iterator's
|
810 | return values. Supports both arrays and objects.
|
811 | The rules specified in [Iteration methods] apply.
|
812 |
|
813 | ```js
|
814 | users =
|
815 | { 12: { name: 'Steve' },
|
816 | 23: { name: 'Bill' } }
|
817 |
|
818 | names = scour(users).map((user, key) => user.get('name'))
|
819 | // => [ 'Steve', 'Bill' ]
|
820 | ```
|
821 |
|
822 | ### mapObject
|
823 |
|
824 | > `mapObject(function(val, key))`
|
825 |
|
826 | Creates a new `Object` with with the results of calling a provided function
|
827 | on every element in this array. Works like [Array#map], but also works on
|
828 | objects as well as arrays, and it returns an object instead.
|
829 | The rules specified in [Iteration methods] apply.
|
830 |
|
831 | See [scour.mapObject()] for details and the non-wrapped version.
|
832 |
|
833 | ### indexedMap
|
834 |
|
835 | > `indexedMap(function(val, key))`
|
836 |
|
837 | Creates a new `Object` with with the results of calling a provided function
|
838 | returning the keys and values for the new object.
|
839 | The rules specified in [Iteration methods] apply.
|
840 |
|
841 | See [scour.indexedMap()] for details and the non-wrapped version.
|
842 |
|
843 | ### reset
|
844 |
|
845 | > `reset(value, options)`
|
846 |
|
847 | Returns a clone with the `value` replaced. The new instance will
|
848 | retain the same properties, so things like [use()] extensions are carried
|
849 | over.
|
850 |
|
851 | ```js
|
852 | db = scour({ name: 'hello' })
|
853 | db.value //=> { name: 'hello' }
|
854 |
|
855 | db = db.reset({})
|
856 | db.value // => {}
|
857 | ```
|
858 |
|
859 | This is useful for, say, using Scour with [Redux] and implementing an
|
860 | action to reset the state back to empty.
|
861 |
|
862 | ## Attributes
|
863 |
|
864 | These attributes are available to [scour] instances.
|
865 |
|
866 | ### value
|
867 |
|
868 | > `value`
|
869 |
|
870 | The raw value being wrapped. You can use this to terminate a chained call.
|
871 |
|
872 | ```js
|
873 | users =
|
874 | [ { name: 'john', admin: true },
|
875 | { name: 'kyle', admin: false } ]
|
876 |
|
877 | scour(users)
|
878 | .filter({ admin: true })
|
879 | .value
|
880 | // => [ { name: 'john', admin: true } ]
|
881 | ```
|
882 |
|
883 | ### root
|
884 |
|
885 | > `root`
|
886 |
|
887 | A reference to the root [scour] instance.
|
888 | Everytime you traverse using [go()], a new [scour] object is spawned that's
|
889 | scoped to a keypath. Each of these [scour] objects have a `root` attribute
|
890 | that's a reference to the top-level [scour] object.
|
891 |
|
892 | ```js
|
893 | db = scour(...)
|
894 |
|
895 | photos = db.go('photos')
|
896 | photos.root // => same as `db`
|
897 | ```
|
898 |
|
899 | This allows you to return to the root when needed.
|
900 |
|
901 | ```js
|
902 | db = scour(...)
|
903 | artist = db.go('artists', '9328')
|
904 | artist.root.go('albums').find({ artist_id: artist.get('id') })
|
905 | ```
|
906 |
|
907 | ### keypath
|
908 |
|
909 | > `keypath`
|
910 |
|
911 | An array of strings representing each step in how deep the current scope is
|
912 | relative to the root. Each time you traverse using [go()], a new [scour]
|
913 | object is spawned.
|
914 |
|
915 | ```js
|
916 | db = scour(...)
|
917 |
|
918 | users = db.go('users')
|
919 | users.keypath // => ['users']
|
920 |
|
921 | admins = users.go('admins')
|
922 | admins.keypath // => ['users', 'admins']
|
923 |
|
924 | user = admins.go('23')
|
925 | user.keypath // => ['users', 'admins', '23']
|
926 | ```
|
927 |
|
928 | ## Utility functions
|
929 |
|
930 | These are utilities that don't need a wrapped object.
|
931 |
|
932 | ### scour.get
|
933 |
|
934 | > `scour.get(object, keypath)`
|
935 |
|
936 | Gets a keypath from an object.
|
937 |
|
938 | ```js
|
939 | data = { users: { bob: { name: 'john' } } }
|
940 |
|
941 | result = get(data, ['users', 'bob', 'name'])
|
942 | // => 'robert'
|
943 | ```
|
944 |
|
945 | This is also available as `require('scourjs/utilities/get')`.
|
946 |
|
947 | ### scour.set
|
948 |
|
949 | > `scour.set(object, keypath, value)`
|
950 |
|
951 | Sets a `keypath` into an `object` immutably.
|
952 |
|
953 | ```js
|
954 | data = { users: { bob: { name: 'john' } } }
|
955 |
|
956 | result = set(data, ['users', 'bob', 'name'], 'robert')
|
957 | // => { users: { bob: { name: 'robert' } } }
|
958 | ```
|
959 |
|
960 | This is also available as `require('scourjs/utilities/set')`.
|
961 |
|
962 | ### scour.del
|
963 |
|
964 | > `scour.del(object, keypath)`
|
965 |
|
966 | Deletes a `keypath` from an `object` immutably.
|
967 |
|
968 | ```js
|
969 | data = { users: { bob: { name: 'robert' } } }
|
970 | result = del(data, ['users', 'bob', 'name'])
|
971 |
|
972 | // => { users: { bob: {} } }
|
973 | ```
|
974 |
|
975 | This is also available as `require('scourjs/utilities/del')`.
|
976 |
|
977 | ### scour.extendIn
|
978 |
|
979 | > `scour.extendIn(object, keypath, extensions...)`
|
980 |
|
981 | Extends a `keypath` from an `object` immutably.
|
982 |
|
983 | ```js
|
984 | data = { users: { bob: { name: 'robert' } } }
|
985 | result = extendIn(data, ['users', 'bob'], { email: 'bob@gmail.com' })
|
986 |
|
987 | // => { users: { bob: { name: 'robert', email: 'bob@gmail.com' } } }
|
988 | ```
|
989 |
|
990 | This is also available as `require('scourjs/utilities/extend_in')`.
|
991 |
|
992 | ### scour.each
|
993 |
|
994 | > `scour.each(iterable, fn)`
|
995 |
|
996 | Iterates through `iterable`, either an object or an array. This is an
|
997 | implementation of [Array#forEach] that also works for objects. The callback
|
998 | `fn` will be invoked with two parameters: `currentValue` and `key`, just
|
999 | like `Array#forEach`.
|
1000 |
|
1001 | This is also available as `require('scourjs/utilities/each')`.
|
1002 |
|
1003 | [Array#forEach]: http://devdocs.io/javascript/global_objects/array/foreach
|
1004 |
|
1005 | ### scour.map
|
1006 |
|
1007 | > `scour.map(iterable, fn)`
|
1008 |
|
1009 | Creates a new `Array` with with the results of calling a provided function
|
1010 | on every element in this array. Works like [Array#map], but also works on
|
1011 | objects as well as arrays.
|
1012 |
|
1013 | The callback `fn` will be invoked with two parameters: `currentValue` and
|
1014 | `key`, just like [Array#map].
|
1015 |
|
1016 | This is also available as `require('scourjs/utilities/map')`.
|
1017 |
|
1018 | [Array#map]: http://devdocs.io/javascript/global_objects/array/map
|
1019 |
|
1020 | ### scour.mapObject
|
1021 |
|
1022 | > `scour.mapObject(iterable, fn)`
|
1023 |
|
1024 | Creates a new `Object` with with the results of calling a provided function
|
1025 | on every element in this array. Works like [Array#map], but also works on
|
1026 | objects as well as arrays, and it returns an object instead.
|
1027 |
|
1028 | The callback `fn` will be invoked with two parameters: `currentValue` and
|
1029 | `key`, just like [Array#map].
|
1030 |
|
1031 | ```js
|
1032 | object = { a: 20, b: 30, c: 40 }
|
1033 | result = scour.mapObject(object, (val, key) => {
|
1034 | return '$' + val + '.00'
|
1035 | })
|
1036 |
|
1037 | // => { a: '$20.00', b: '$30.00', c: '$40.00' }
|
1038 | ```
|
1039 |
|
1040 | This is also available as `require('scourjs/utilities/map_object')`.
|
1041 |
|
1042 | ### scour.indexedMap
|
1043 |
|
1044 | > `scour.indexedMap(iterable, fn)`
|
1045 |
|
1046 | Creates a new `Object` with with the results of calling a provided function
|
1047 | returning the keys and values for the new object.
|
1048 |
|
1049 | The callback `fn` will be invoked with two parameters: `currentValue` and
|
1050 | `key`, just like [Array#map].
|
1051 |
|
1052 | The callback `fn` should return an array with two elements: with `result[0]`
|
1053 | being the key, and `result[1]` being the value. These are what the new
|
1054 | object will be constructed with.
|
1055 |
|
1056 | The `iterable` parameter can be an object or an array. This works like
|
1057 | `Array#map`, but also works on objects as well as arrays.
|
1058 |
|
1059 | ```js
|
1060 | list = ['Fred', 'Barney', 'Wilma']
|
1061 |
|
1062 | object = scour.indexedMap(list, (val, key) => {
|
1063 | var newkey = val.substr(0, 1)
|
1064 | return [ newkey, val ]
|
1065 | })
|
1066 |
|
1067 | // => { f: 'Fred', b: 'Barney', w: 'Wilma' }
|
1068 | ```
|
1069 |
|
1070 | This is also available as `require('scourjs/utilities/indexed_map')`.
|
1071 |
|
1072 | ### scour.filter
|
1073 |
|
1074 | > `scour.filter(iterable, function(val, key), [isArray])`
|
1075 |
|
1076 | Creates a new Array or Object with all elements that pass the test
|
1077 | implemented by the provided function.
|
1078 |
|
1079 | Works like [Array#filter], but will return an object if an object is also passed.
|
1080 |
|
1081 | The optional `isArray` argument, when passed `true`, will always make this
|
1082 | return an `Array`. If `false`, it will always be an `Object`. Leave it
|
1083 | `undefined` for the default behavior.
|
1084 |
|
1085 | This is also available as `require('scourjs/utilities/filter')`.
|
1086 |
|
1087 | [Array#filter]: http://devdocs.io/javascript/global_objects/array/filter
|
1088 |
|
1089 | ### scour.sortBy
|
1090 |
|
1091 | > `scour.sortBy(iterable, criteria)`
|
1092 |
|
1093 | Sorts by a given criteria.
|
1094 |
|
1095 | ```js
|
1096 | list = [ { name: 'Fred' }, { name: 'Barney' }, { name: 'Wilma' } ]
|
1097 | scour.sortBy(list, 'name')
|
1098 | ```
|
1099 |
|
1100 | This is also available as `require('scourjs/utilities/sort_by')`.
|
1101 |
|
1102 |
|
1103 | [at()]: #at
|
1104 | [del()]: #del
|
1105 | [extend()]: #extend
|
1106 | [filter()]: #filter
|
1107 | [forEach()]: #foreach
|
1108 | [get()]: #get
|
1109 | [getAt()]: #getat
|
1110 | [go()]: #go
|
1111 | [keypath]: #keypath
|
1112 | [len()]: #len
|
1113 | [map()]: #map
|
1114 | [root]: #root
|
1115 | [scour]: #scour
|
1116 | [set()]: #set
|
1117 | [toArray()]: #toarray
|
1118 | [value]: #value
|
1119 | [use()]: #use
|
1120 | [scour.mapObject()]: #scour.mapobject
|
1121 | [scour.indexedMap()]: #scour.indexedmap
|
1122 | [scour.filter()]: #scour-filter
|
1123 | [Iteration methods]: #iteration-methods
|
1124 | [on null values]: #on-null-values
|
1125 |
|
1126 | [Extensions example]: docs/extensions_example.md
|
1127 | [Object.assign]: https://devdocs.io/javascript/global_objects/object/assign
|
1128 | [sift.js]: https://www.npmjs.com/package/sift
|
1129 | [Redux]: http://rackt.github.io/redux
|
1130 | [Immutable.js]: http://facebook.github.io/immutable-js/
|
1131 |
|
1132 | ## Thanks
|
1133 |
|
1134 | **scour** © 2015+, Rico Sta. Cruz. Released under the [MIT] License.<br>
|
1135 | Authored and maintained by Rico Sta. Cruz with help from contributors ([list][contributors]).
|
1136 |
|
1137 | > [ricostacruz.com](http://ricostacruz.com) ·
|
1138 | > GitHub [@rstacruz](https://github.com/rstacruz) ·
|
1139 | > Twitter [@rstacruz](https://twitter.com/rstacruz)
|
1140 |
|
1141 | [MIT]: http://mit-license.org/
|
1142 | [contributors]: http://github.com/rstacruz/scour/contributors
|