UNPKG

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