{"id":13518759,"url":"https://github.com/rstacruz/scour","last_synced_at":"2025-04-06T03:12:29.780Z","repository":{"id":66010302,"uuid":"48351219","full_name":"rstacruz/scour","owner":"rstacruz","description":"Traverse objects and arrays with ease","archived":false,"fork":false,"pushed_at":"2020-07-17T13:28:41.000Z","size":358,"stargazers_count":307,"open_issues_count":10,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-30T02:07:04.430Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://ricostacruz.com/scour","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rstacruz.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-12-21T04:47:50.000Z","updated_at":"2024-12-10T13:09:31.000Z","dependencies_parsed_at":"2023-05-19T23:30:53.105Z","dependency_job_id":null,"html_url":"https://github.com/rstacruz/scour","commit_stats":{"total_commits":237,"total_committers":3,"mean_commits":79.0,"dds":"0.10126582278481011","last_synced_commit":"ec6680acfc3d0ce1d445037e29f33c5550820d76"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rstacruz%2Fscour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rstacruz%2Fscour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rstacruz%2Fscour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rstacruz%2Fscour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rstacruz","download_url":"https://codeload.github.com/rstacruz/scour/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427012,"owners_count":20937214,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T05:01:48.645Z","updated_at":"2025-04-06T03:12:29.765Z","avatar_url":"https://github.com/rstacruz.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Libraries"],"sub_categories":["[Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)"],"readme":"\u003e ## End-of-life\n\u003e\n\u003e July 2020: Thank you to all the users and contributors who have made this project possible. Moving forward, please consider alternatives such as [immer](https://www.npmjs.com/package/immer).\n\n---\n\n# scour.js\n\n\u003c!-- {.massive-header.-with-tagline} --\u003e\n\n\u003e Traverse objects and arrays immutably\n\nScour is a general-purpose library for dealing with JSON trees.\u003cbr\u003e\nAs a simple utility with a broad purpose, it can be used to solve many problems. Use it to:\n\n- Manage your [Redux] datastore.\n- Provide a model layer to access data in your single-page app. [→](#models)\n- Navigate a large JSON tree easily.\n- Rejoice in having a lightweight alternative to [Immutable.js]. ([Compare](docs/comparison.md))\n\n[![Status](https://travis-ci.org/rstacruz/scour.svg?branch=master)](https://travis-ci.org/rstacruz/scour \"See test builds\")\n\n## Install\n\n```sh\nnpm install --save-exact scourjs\n```\n\n```js\nwindow.scour                       // non commonjs\nconst scour = require('scourjs')   // commonjs/node\nimport scour from 'scourjs'        // es6 modules\n```\n\n## Features\n\nCalling `scour(object)` returns a wrapper that you can use to traverse `object`.\nUse [get()](#get) to retrieve values.\n\n```js\ndata =\n  { users:\n    { 1: { name: 'john' },\n      2: { name: 'shane', confirmed: true },\n      3: { name: 'barry', confirmed: true } } }\n```\n\n```js\nscour(data).get('users', '1', 'name')   // =\u003e 'john'\n```\n\n\u003cbr\u003e\n\n### Traversal\nUse [go()](#go) to dig into the structure. It will return another `scour`\nwrapper scoped to that object.\n\n```js\ndata =\n  { users:\n    { admins:\n      { bob: { logged_in: true },\n        sue: { logged_in: false } } } }\n```\n\n```js\nusers  = scour(data).go('users')            // =\u003e [scour (admins)]\nadmins = scour(data).go('users', 'admins')  // =\u003e [scour (bob, sue)]\n\nadmins.go('bob').get('logged_in')           // =\u003e true\n```\n\n\u003cbr\u003e\n\n### Chaining\n\n`scour()` provides a wrapper that can be used to chain methods. This is inspired by [Underscore] and [Lodash].\n\n```js\nscour(data)\n  .go('users')\n  .filter({ admin: true })\n  .value\n```\n\n[Underscore]: http://underscorejs.org/\n[Lodash]: http://lodash.com/\n\n\u003cbr\u003e\n\n### Immutable modifications\n\nUse [set()](#set) to update values. Scout treats all data as immutable, so this\ndoesn't modify your original `data`, but gets you a new one with the\nmodifications made.\n\n```js\ndata = scour(data)\n  .set(['users', '1', 'updated_at'], +new Date())\n  .value\n\n// =\u003e { users:\n//      { 1: { name: 'john', updated_at: 1450667171188 },\n//        2: { name: 'shane', confirmed: true },\n//        3: { name: 'barry', confirmed: true } } }\n```\n\n\u003cbr\u003e\n\n### Advanced traversing\n\nUse [filter()] to filter results with advanced querying.\n\n```js\nusers = scour(data).go('users')\n\nusers\n  .filter({ confirmed: true })\n  .at(0)\n  .get('name')   // =\u003e 'shane'\n```\n\n\u003cbr\u003e\n\n### Models\n\nUse [use()](#use) to add your own methods to certain keypaths. This makes them behave like models.\u003cbr\u003e\nSee [a detailed example](docs/extensions_example.md) to learn more.\n\n##### Sample data\n\n\u003c!-- {.file-heading} --\u003e\n\n```js\ndata =\n  { artists:\n    { 1: { first_name: 'Louie', last_name: 'Armstrong' },\n      2: { first_name: 'Miles', last_name: 'Davis' } } }\n```\n\n##### Your models\n\n\u003c!-- {.file-heading} --\u003e\n\n```js\nRoot = {\n  artists () { return this.go('artists') }\n}\n\nArtist = {\n  fullname () {\n    return this.get('first_name') + ' ' + this.get('last_name')\n  }\n}\n```\n\n##### Using with scour\n\n\u003c!-- {.file-heading} --\u003e\n\n```js\ndb = scour(data)\n  .use({\n    '': Root,\n    'artists.*': Artist\n  })\n\ndb.artists().find({ name: 'Miles' }).fullname()\n//=\u003e 'Miles Davis'\n```\n\n\u003cbr\u003e\n\n## API\n\n\u003c!--api--\u003e\n\n### scour\n\n\u003e `scour(object)`\n\nReturns a scour instance wrapping `object`.\n\n```js\nscour(obj)\n```\n\nIt can be called on any Object or Array. (In fact, it can be called on\nanything, but is only generally useful for Objects and Arrays.)\n\n```js\ndata = { menu: { visible: true, position: 'left' } }\nscour(data).get('menu.visible')\n\nlist = [ { id: 2 }, { id: 5 }, { id: 12 } ]\nscour(list).get('0.id')\n```\n\n__Chaining__:\nYou can use it to start method chains. In fact, the intended use is to keep\nyour root [scour] object around, and chain from this.\n\n```js\ndb = scour({ menu: { visible: true, position: 'left' } })\n\n// Elsewhere:\nmenu = db.go('menu')\nmenu.get('visible')\n```\n\n__Properties__:\nIt the [root], [value] and [keypath] properties.\n\n```js\ns = scour(obj)\ns.root             // =\u003e [scour object]\ns.value            // =\u003e raw data (that is, `obj`)\ns.keypath          // =\u003e string array\n```\n\n__Accessing the value:__\nYou can access the raw data using [value].\n\n```js\ndb = scour(data)\ndb.value               // =\u003e same as `data`\ndb.go('users').value   // =\u003e same as `data.users`\n```\n\n## Chaining methods\n\nThese methods are used to traverse nested structures. All these\nmethods return [scour] instances, making them suitable for chaining.\n\n#### On null values\nNote that `undefined`, `false` and `null` values are still [scour]-wrapped\nwhen returned from [go()], [at()] and [find()].\n\n```js\nlist = [ { name: 'Homer' }, { name: 'Bart' } ]\n\nscour(list).at(4)         // =\u003e [ scour undefined ]\nscour(list).at(4).value   // =\u003e undefined\n```\n\nThis is done so that you can chain methods safely even when something is null.\nThis behavior is consistent with what you'd expect with jQuery.\n\n```js\ndata = { users: { ... } }\ndb = scour(data)\n\ndb.go('blogposts').map((post) =\u003e post.get('title'))\n// =\u003e []\n```\n\n### go\n\n\u003e `go(keypath...)`\n\nNavigates down to a given `keypath`. Always returns a [scour] instance.\nRules [on null values] apply.\n\n```js\ndata =\n  { users:\n    { 12: { name: 'steve', last: 'jobs' },\n      23: { name: 'bill', last: 'gates' } } }\n\nscour(data).go('users')                    // =\u003e [scour (users)]\nscour(data).go('users', '12')              // =\u003e [scour (name, last)]\nscour(data).go('users', '12').get('name')  // =\u003e 'steve'\n```\n\n__Dot notation:__\nKeypaths can be given in dot notation or as an array. These statements are\nequivalent.\n\n```js\nscour(data).go('users.12')\nscour(data).go('users', '12')\nscour(data).go(['users', '12'])\n```\n\n__Non-objects:__\nIf you use it on a non-object or non-array value, it will still be\nreturned as a [scour] instance. This is not likely what you want; use\n[get()] instead.\n\n```js\nattr = scour(data).go('users', '12', 'name')\nattr           // =\u003e [scour object]\nattr.value     // =\u003e 'steve'\nattr.keypath   // =\u003e ['users', '12', 'name']\n```\n\n### at\n\n\u003e `at(index)`\n\nReturns the item at `index`. This differs from `go` as this searches by\nindex, not by key. This returns a the raw value, unlike [getAt()]. Rules\n[on null values] apply.\n\n```js\nusers =\n  { 12: { name: 'steve' },\n    23: { name: 'bill' } }\n\nscour(users).at(0)          // =\u003e [scour { name: 'steve' }]\nscour(users).get(12)        // =\u003e [scour { name: 'steve' }]\n```\n\n### getAt\n\n\u003e `getAt(index)`\n\nReturns the item at `index`. This differs from `get` as this searches by\nindex, not by key. This returns a the raw value, unlike [at()].\n*(Since v0.5)*\n\n```js\nusers =\n  { 12: { name: 'steve' },\n    23: { name: 'bill' } }\n\nscour(users).at(0)           // =\u003e [scour { name: 'steve' }]\nscour(users).getAt(0)        // =\u003e { name: 'steve' }\n```\n\n### filter\n\n\u003e `filter(conditions)`\n\nSifts through the values and returns a set that matches given\n`conditions`. Supports simple objects, MongoDB-style\nqueries, and functions.\n\n```js\nscour(data).filter({ name: 'Moe' })\nscour(data).filter({ name: { $in: ['Larry', 'Curly'] })\nscour(data).filter((item) =\u003e item.get('name') === 'Moe')\n```\n\n__Filter by object:__\nIf you pass an object as a condition, `filter()` will check if that object\ncoincides with the objects in the collection.\n\n```js\nscour(data).filter({ name: 'Moe' })\n```\n\n__Filter by function:__\nYou may pass a function as a parameter. In this case, the `item` being\npassed to the callback will be a [scour]-wrapped object. The result\nwill also be a [scour]-wrapped object, making it chainable.\n\n```js\nscour(data)\n  .filter((item, key) =\u003e +item.get('price') \u003e 200)\n  .sortBy('price')\n  .first()\n```\n\n__Advanced queries:__\nMongoDB-style queries are supported as provided by [sift.js].  For\nreference, see [MongoDB Query Operators][query-ops].\n\n```js\nscour(products).filter({ price: { $gt: 200 })\nscour(articles).filter({ published_at: { $not: null }})\n```\n\n__Arrays or objects:__\nBoth arrays and array-like objects are supported. In this example below,\nan object will be used as the input.\n\n```js\ndevices =\n  { 1: { id: 1, name: 'Phone', mobile: true },\n    2: { id: 2, name: 'Tablet', mobile: true },\n    3: { id: 3, name: 'Desktop', mobile: false } }\n\nscour(devices).filter({ mobile: true }).len()\n// =\u003e 2\n```\n\nAlso see [scour.filter()] for the unwrapped version.\n\n[query-ops]: https://docs.mongodb.org/manual/reference/operator/query/\n\n### reject\n\n\u003e `reject(conditions)`\n\nInverse of [filter()] -- see `filter()` documentation for details.\n\n### find\n\n\u003e `find(conditions)`\n\nReturns the first value that matches `conditions`.  Supports MongoDB-style\nqueries. For reference, see [MongoDB Query Operators][query-ops]. Also\nsee [filter()], as this is functionally-equivalent to the first result of\n`filter()`. Rules [on null values] apply.\n\n[query-ops]: https://docs.mongodb.org/manual/reference/operator/query/\n\n```js\nscour(data).find({ name: 'john' })\nscour(data).find({ name: { $in: ['moe', 'larry'] })\n```\n\n### first\n\n\u003e `first()`\n\nReturns the first result as a [scour]-wrapped object. This is equivalent\nto [at(0)](#at).\n\n### last\n\n\u003e `last()`\n\nReturns the first result as a [scour]-wrapped object. This is equivalent\nto `at(len() - 1)`: see [at()] and [len()].\n\n### sortBy\n\n\u003e `sortBy(condition)`\n\nSorts a collection. Returns a [scour]-wrapped object suitable for\nchaining. Like other chainable methods, this works on arrays as well as\nobjects. *(Since v0.8)*\n\n```js\ndata =\n  { 0: { name: 'Wilma' },\n    1: { name: 'Barney' },\n    2: { name: 'Fred' } }\n\nscour(data).sortBy('name').value\n// { 1: { name: 'Barney' },\n//   2: { name: 'Fred' },\n//   0: { name: 'Wilma' } }\n```\n\n__Conditions:__\nThe given condition can be a string or a function. When it's given as a\nfunction, the `item` being passed is a [scour]-wrapped object, just like\nin [forEach()] (et al). These two examples below are\nfunctionally-equivalent.\n\n```js\nscour(data).sortBy('name')\nscour(data).sortBy((item) =\u003e item.get('name'))\n```\n\nYou may also define nested keys in dot-notation:\n\n```js\nscour(data).sortBy('user.name')\n```\n\n## Reading methods\n\nFor retrieving data.\n\n### get\n\n\u003e `get(keypath...)`\n\nReturns data in a given `keypath`.\n\n```js\ndata =\n  { users:\n    { 12: { name: 'steve' },\n      23: { name: 'bill' } } }\n\nscour(data).get('users')       // =\u003e same as data.users\nscour(data).go('users').value  // =\u003e same as data.users\n```\n\n__Dot notation:__\nLike [go()], the `keypath` can be given in dot notation.\n\n```js\nscour(data).get('books.featured.name')\nscour(data).get('books', 'featured', 'name')\n```\n\n### len\n\n\u003e `len()`\n\nReturns the length of the object or array. For objects, it returns the\nnumber of keys.\n\n```js\nusers =\n  { 12: { name: 'steve' },\n    23: { name: 'bill' } }\n\nnames = scour(users).len()  // =\u003e 2\n```\n\n### toArray\n\n\u003e `toArray()`\n\nReturns an array. If the the value is an object, it returns the values of\nthat object. If the value is an array, it returns it as is. Also aliased\nas `values()`.\n\n```js\nusers =\n  { 12: { name: 'steve' },\n    23: { name: 'bill' } }\n\nnames = scour(users).toArray()\n// =\u003e [ {name: 'steve'}, {name: 'bill'} ]\n```\n\n### keys\n\n\u003e `keys()`\n\nReturns keys. If the value is an array, this returns the array's indices.\nAlso see [toArray()] to retrieve the values instead.\n\n## Writing methods\n\nThese are methods for modifying an object/array tree immutably.\nNote that all these functions are immutable--it will not modify existing\ndata, but rather spawn new objects with the modifications done on them.\n\n### set\n\n\u003e `set(keypath, value)`\n\nSets values immutably. Returns a copy of the same object ([scour]-wrapped)\nwith the modifications applied.\n\n```js\ndata = { bob: { name: 'Bob' } }\ndb = scour(data)\ndb = db.set([ 'bob', 'name' ], 'Robert')\n// db.value == { bob: { name: 'Robert' } }\n```\n\n__Immutability:__\nThis is an immutable function, and will return a new object. It won't\nmodify your original object.\n\n```js\nprofile = scour({ name: 'John' })\nprofile2 = profile.set('email', 'john@gmail.com')\n\nprofile.value   // =\u003e { name: 'John' }\nprofile2.value  // =\u003e { name: 'John', email: 'john@gmail.com' }\n```\n\n__Using within a scope:__\nBe aware that using all writing methods ([set()], [del()], [extend()]) on\nscoped objects (ie, made with [go()]) will spawn a new [root] object. If\nyou're keeping a reference to the root object, you'll need to update it\naccordingly.\n\n```js\ndb = scour(data)\nbook = db.go('book')\nbook.root === db       // correct so far\n\nbook = book.set('title', 'IQ84')\nbook = book.del('sale_price')\nbook.root !== db      // `root` has been updated\n```\n\n__Dot notation:__\nLike [go()] and [get()], the keypath can be given in dot notation or an\narray.\n\n```js\nscour(data).set('menu.left.visible', true)\nscour(data).set(['menu', 'left', 'visible'], true)\n```\n\n### del\n\n\u003e `del(keypath)`\n\nDeletes values immutably. Returns a copy of the same object\n([scour]-wrapped) with the modifications applied.\n\nLike [set()], the keypath can be given in dot notation or an\narray.\n\n```js\nscour(data).del('menu.left.visible')\nscour(data).del(['menu', 'left', 'visible'])\n```\n\nSee [set()] for more information on working with immutables.\n\n### extend\n\n\u003e `extend(objects...)`\n\nExtends the data with more values. Returns a [scour]-wrapped object. Just\nlike [Object.assign], you may pass multiple objects to the parameters.\n\n```js\ndata  = { a: 1, b: 2 }\ndata2 = scour(data).extend({ c: 3 })\n```\n\n```js\ndata2  // =\u003e [scour { a: 1, b: 2, c: 3 }]\ndata2.value   // =\u003e { a: 1, b: 2, c: 3 }\n```\n\nWhen used with anything non-object, it will be overridden.\n\n```js\ndata = {}\ndb = scour(data)\ndb = db.go('state').extend({ pressed: true }).root\n\ndb.value  // =\u003e { state: { pressed: true } }\n```\n\nSee [set()] for more information on working with immutables.\n\n## Utility methods\n\nFor stuff.\n\n### use\n\n\u003e `use(extensions)`\n\nExtends functionality for certain keypaths with custom methods.\nSee [Extensions example] for examples.\n\n```js\ndata =\n  { users:\n    { 12: { name: 'steve', surname: 'jobs' },\n      23: { name: 'bill', surname: 'gates' } } }\n\nextensions = {\n  'users.*': {\n    fullname () {\n      return this.get('name') + ' ' + this.get('surname')\n    }\n  }\n}\n\nscour(data)\n  .use(extensions)\n  .get('users', 12)\n  .fullname()       // =\u003e 'bill gates'\n```\n\n__Extensions format:__\nThe parameter `extension` is an object, with keys being keypath globs, and\nvalues being properties to be extended.\n\n```js\n.use({\n  'books.*': { ... },\n  'authors.*': { ... },\n  'publishers.*': { ... }\n })\n```\n\n__Extending root:__\nTo bind properties to the root method, use an empty string as the keypath.\n\n```js\n.use({\n  '': {\n    users() { return this.go('users') },\n    authors() { return this.go('authors') }\n  }\n})\n```\n\n__Keypath filtering:__\nYou can use glob-like `*` and `**` to match parts of a keypath. A `*` will\nmatch any one segment, and `**` will match one or many segments. Here are\nsome examples:\n\n- `users.*` - will match `users.1`, but not `users.1.photos`\n- `users.**` - will match `users.1.photos`\n- `users.*.photos` - will match `users.1.photos`\n- `**` will match anything\n\n__When using outside root:__\nAny extensions in a scoped object (ie, made with [go()]) will be used relative\nto it. For instance, if you define an extension to `admins.*` inside\n`.go('users')`, it will affect `users.\n\n```js\ndata = { users: { john: { } }\ndb = scour(data)\n\nusers = db.go('users')\n  .use({ '*': { hasName () { return !!this.get('name') } })\n\nusers.go('john').hasName()      // works\n```\n\nWhile this is supported, it is *not* recommended: these extensions will not\npropagate back to the root, and any objects taken from the root will not\nhave those extensions applied to them.\n\n```js\nusers.go('john').hasName()              // works\ndb.go('users.john').hasName()           // doesn't work\n```\n\n### index\n\n\u003e `index(keypath, field)`\n\nSets up indices to improve [filter()] performance. *(Since v0.12)*\n\n- `keypath` *(String | Array)* - the keypath of the collection.\n- `field` *(String)* - the name of the field to be indexed.\n\n```js\ndata =\n  { users:\n    { 1: { name: 'John Creamer' },\n      2: { name: 'Stephane K' } } }\n\ndb = scour(data).index('users', 'name')\ndb.filter({ name: 'Stephane K' })\n```\n\nDoing this will add an index in the root (acccessible via\n`scour().indices`) to make searches faster for certain [filter()] queries.\nAny writing actions ([set()], [extend()], [del()]) will automatically\nupdate the index.\n\nSee [scour-search] for more information on indexing.\n\n[scour-search]: https://github.com/rstacruz/scour-search\n\n### toJSON\n\n\u003e `toJSON()`\n\nReturns the value for serialization. This allows `JSON.stringify()` to\nwork with `scour`-wrapped objects. The name of this method is a bit\nconfusing, as it doesn't actually return a JSON string — but I'm afraid\nthat it's the way that the JavaScript API for [JSON.stringify] works.\n\n[JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior\n\n### equal\n\n\u003e `equal(other)`\n\nChecks for equality between two Scour-wrapped objects.\n\n```js\na = scour(data)\nb = scour(data)\n\na.equal(b)   // =\u003e true\n```\n\n## Iteration methods\n\nThese methods are generally useful for collections. These\nmethods can work with either arrays or array-like objects, such as\nbelow.\n\n```js\nsubjects =\n  { 1: { id: 1, title: 'Math', level: 101 },\n    2: { id: 2, title: 'Science', level: 103 },\n    3: { id: 3, title: 'History', level: 102 } }\n```\n\n__Values:__\nFor all these functions, The items passed onto the callbacks _is_ a\n[scour]-wrapped object. Use `item.value` or `this` to access the raw\nvalues.\n\n```js\nscour(subjects).forEach((subject, key) =\u003e {\n  console.log(subject.get('title'))\n})\n```\n\n__Return values:__\nFor methods that return values (such as [map()], the returned results _is\nnot_ a [scour]-wrapped object, and isn't suitable for chaining.\n\n```js\nscour(subjects).map((subject, key) =\u003e {\n  return subject.get('title') + ' ' + subject.get('level')\n})\n// =\u003e [ 'Math 101', 'Science 103', 'History 102' ]\n```\n\n### forEach\n\n\u003e `forEach(function(item, key, index))`\n\nLoops through each item. Supports both arrays and objects.\nThe rules specified in [Iteration methods] apply.\n\n```js\nusers =\n  { 12: { name: 'steve' },\n    23: { name: 'bill' } }\n\nscour(users).each((user, key) =\u003e {\n  console.log(user.get('name'))\n})\n```\n\nThe values passed onto the function are:\n\n- `item` - the value; always a scour object.\n- `key` - the key.\n- `index` - the index.\n\n### each\n\n\u003e `each(fn)`\n\nAlias for [forEach](#foreach).\n\n### map\n\n\u003e `map(function(item, key))`\n\nLoops through each item and returns an array based on the iterator's\nreturn values. Supports both arrays and objects.\nThe rules specified in [Iteration methods] apply.\n\n```js\nusers =\n  { 12: { name: 'Steve' },\n    23: { name: 'Bill' } }\n\nnames = scour(users).map((user, key) =\u003e user.get('name'))\n// =\u003e [ 'Steve', 'Bill' ]\n```\n\n### mapObject\n\n\u003e `mapObject(function(val, key))`\n\nCreates a new `Object` with with the results of calling a provided function\non every element in this array. Works like [Array#map], but also works on\nobjects as well as arrays, and it returns an object instead.\nThe rules specified in [Iteration methods] apply.\n\nSee [scour.mapObject()] for details and the non-wrapped version.\n\n### indexedMap\n\n\u003e `indexedMap(function(val, key))`\n\nCreates a new `Object` with with the results of calling a provided function\nreturning the keys and values for the new object.\nThe rules specified in [Iteration methods] apply.\n\nSee [scour.indexedMap()] for details and the non-wrapped version.\n\n### reset\n\n\u003e `reset(value, options)`\n\nReturns a clone with the `value` replaced. The new instance will\nretain the same properties, so things like [use()] extensions are carried\nover.\n\n```js\ndb = scour({ name: 'hello' })\ndb.value  //=\u003e { name: 'hello' }\n\ndb = db.reset({})\ndb.value  // =\u003e {}\n```\n\nThis is useful for, say, using Scour with [Redux] and implementing an\naction to reset the state back to empty.\n\n## Attributes\n\nThese attributes are available to [scour] instances.\n\n### value\n\n\u003e `value`\n\nThe raw value being wrapped. You can use this to terminate a chained call.\n\n```js\nusers =\n  [ { name: 'john', admin: true },\n    { name: 'kyle', admin: false } ]\n\nscour(users)\n  .filter({ admin: true })\n  .value\n// =\u003e [ { name: 'john', admin: true } ]\n```\n\n### root\n\n\u003e `root`\n\nA reference to the root [scour] instance.\nEverytime you traverse using [go()], a new [scour] object is spawned that's\nscoped to a keypath.  Each of these [scour] objects have a `root` attribute\nthat's a reference to the top-level [scour] object.\n\n```js\ndb = scour(...)\n\nphotos = db.go('photos')\nphotos.root    // =\u003e same as `db`\n```\n\nThis allows you to return to the root when needed.\n\n```js\ndb = scour(...)\nartist = db.go('artists', '9328')\nartist.root.go('albums').find({ artist_id: artist.get('id') })\n```\n\n### keypath\n\n\u003e `keypath`\n\nAn array of strings representing each step in how deep the current scope is\nrelative to the root. Each time you traverse using [go()], a new [scour]\nobject is spawned.\n\n```js\ndb = scour(...)\n\nusers = db.go('users')\nusers.keypath            // =\u003e ['users']\n\nadmins = users.go('admins')\nadmins.keypath           // =\u003e ['users', 'admins']\n\nuser = admins.go('23')\nuser.keypath             // =\u003e ['users', 'admins', '23']\n```\n\n## Utility functions\n\nThese are utilities that don't need a wrapped object.\n\n### scour.get\n\n\u003e `scour.get(object, keypath)`\n\nGets a keypath from an object.\n\n```js\ndata = { users: { bob: { name: 'john' } } }\n\nresult = get(data, ['users', 'bob', 'name'])\n// =\u003e 'robert'\n```\n\nThis is also available as `require('scourjs/utilities/get')`.\n\n### scour.set\n\n\u003e `scour.set(object, keypath, value)`\n\nSets a `keypath` into an `object` immutably.\n\n```js\ndata = { users: { bob: { name: 'john' } } }\n\nresult = set(data, ['users', 'bob', 'name'], 'robert')\n// =\u003e { users: { bob: { name: 'robert' } } }\n```\n\nThis is also available as `require('scourjs/utilities/set')`.\n\n### scour.del\n\n\u003e `scour.del(object, keypath)`\n\nDeletes a `keypath` from an `object` immutably.\n\n```js\ndata = { users: { bob: { name: 'robert' } } }\nresult = del(data, ['users', 'bob', 'name'])\n\n// =\u003e { users: { bob: {} } }\n```\n\nThis is also available as `require('scourjs/utilities/del')`.\n\n### scour.extendIn\n\n\u003e `scour.extendIn(object, keypath, extensions...)`\n\nExtends a `keypath` from an `object` immutably.\n\n```js\ndata = { users: { bob: { name: 'robert' } } }\nresult = extendIn(data, ['users', 'bob'], { email: 'bob@gmail.com' })\n\n// =\u003e { users: { bob: { name: 'robert', email: 'bob@gmail.com' } } }\n```\n\nThis is also available as `require('scourjs/utilities/extend_in')`.\n\n### scour.each\n\n\u003e `scour.each(iterable, fn)`\n\nIterates through `iterable`, either an object or an array. This is an\nimplementation of [Array#forEach] that also works for objects. The callback\n`fn` will be invoked with two parameters: `currentValue` and `key`, just\nlike `Array#forEach`.\n\nThis is also available as `require('scourjs/utilities/each')`.\n\n[Array#forEach]: http://devdocs.io/javascript/global_objects/array/foreach\n\n### scour.map\n\n\u003e `scour.map(iterable, fn)`\n\nCreates a new `Array` with with the results of calling a provided function\non every element in this array. Works like [Array#map], but also works on\nobjects as well as arrays.\n\nThe callback `fn` will be invoked with two parameters: `currentValue` and\n`key`, just like [Array#map].\n\nThis is also available as `require('scourjs/utilities/map')`.\n\n[Array#map]: http://devdocs.io/javascript/global_objects/array/map\n\n### scour.mapObject\n\n\u003e `scour.mapObject(iterable, fn)`\n\nCreates a new `Object` with with the results of calling a provided function\non every element in this array. Works like [Array#map], but also works on\nobjects as well as arrays, and it returns an object instead.\n\nThe callback `fn` will be invoked with two parameters: `currentValue` and\n`key`, just like [Array#map].\n\n```js\nobject = { a: 20, b: 30, c: 40 }\nresult = scour.mapObject(object, (val, key) =\u003e {\n  return '$' + val + '.00'\n})\n\n// =\u003e { a: '$20.00', b: '$30.00', c: '$40.00' }\n```\n\nThis is also available as `require('scourjs/utilities/map_object')`.\n\n### scour.indexedMap\n\n\u003e `scour.indexedMap(iterable, fn)`\n\nCreates a new `Object` with with the results of calling a provided function\nreturning the keys and values for the new object.\n\nThe callback `fn` will be invoked with two parameters: `currentValue` and\n`key`, just like [Array#map].\n\nThe callback `fn` should return an array with two elements: with `result[0]`\nbeing the key, and `result[1]` being the value. These are what the new\nobject will be constructed with.\n\nThe `iterable` parameter can be an object or an array. This works like\n`Array#map`, but also works on objects as well as arrays.\n\n```js\nlist = ['Fred', 'Barney', 'Wilma']\n\nobject = scour.indexedMap(list, (val, key) =\u003e {\n  var newkey = val.substr(0, 1)\n  return [ newkey, val ]\n})\n\n// =\u003e { f: 'Fred', b: 'Barney', w: 'Wilma' }\n```\n\nThis is also available as `require('scourjs/utilities/indexed_map')`.\n\n### scour.filter\n\n\u003e `scour.filter(iterable, function(val, key), [isArray])`\n\nCreates a new Array or Object with all elements that pass the test\nimplemented by the provided function.\n\nWorks like [Array#filter], but will return an object if an object is also passed.\n\nThe optional `isArray` argument, when passed `true`, will always make this\nreturn an `Array`. If `false`, it will always be an `Object`. Leave it\n`undefined` for the default behavior.\n\nThis is also available as `require('scourjs/utilities/filter')`.\n\n[Array#filter]: http://devdocs.io/javascript/global_objects/array/filter\n\n### scour.sortBy\n\n\u003e `scour.sortBy(iterable, criteria)`\n\nSorts by a given criteria.\n\n```js\nlist = [ { name: 'Fred' }, { name: 'Barney' }, { name: 'Wilma' } ]\nscour.sortBy(list, 'name')\n```\n\nThis is also available as `require('scourjs/utilities/sort_by')`.\n\u003c!--api:end--\u003e\n\n[at()]: #at\n[del()]: #del\n[extend()]: #extend\n[filter()]: #filter\n[forEach()]: #foreach\n[get()]: #get\n[getAt()]: #getat\n[go()]: #go\n[keypath]: #keypath\n[len()]: #len\n[map()]: #map\n[root]: #root\n[scour]: #scour\n[set()]: #set\n[toArray()]: #toarray\n[value]: #value\n[use()]: #use\n[scour.mapObject()]: #scour.mapobject\n[scour.indexedMap()]: #scour.indexedmap\n[scour.filter()]: #scour-filter\n[Iteration methods]: #iteration-methods\n[on null values]: #on-null-values\n\n[Extensions example]: docs/extensions_example.md\n[Object.assign]: https://devdocs.io/javascript/global_objects/object/assign\n[sift.js]: https://www.npmjs.com/package/sift\n[Redux]: http://rackt.github.io/redux\n[Immutable.js]: http://facebook.github.io/immutable-js/\n[scour-search]: https://github.com/rstacruz/scour-search\n\n## Thanks\n\n**scour** © 2015+, Rico Sta. Cruz. Released under the [MIT] License.\u003cbr\u003e\nAuthored and maintained by Rico Sta. Cruz with help from contributors ([list][contributors]).\n\n\u003e [ricostacruz.com](http://ricostacruz.com) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e GitHub [@rstacruz](https://github.com/rstacruz) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e Twitter [@rstacruz](https://twitter.com/rstacruz)\n\n[MIT]: http://mit-license.org/\n[contributors]: http://github.com/rstacruz/scour/contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frstacruz%2Fscour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frstacruz%2Fscour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frstacruz%2Fscour/lists"}