selectn
Version:
Curried property accessor function that resolves deeply-nested object properties via dot/bracket-notation string path while mitigating TypeErrors via friendly and composable API.
244 lines (174 loc) • 7.59 kB
Markdown
# selectn
> Curried [property accessor][Property accessors] function that resolves deeply-nested object properties via dot/bracket-notation string path while mitigating `TypeErrors` via friendly and composable API.
[](https://github.com/wilmoore/selectn.js)
[](https://codeclimate.com/github/wilmoore/selectn.js) [](https://standardjs.com) [](https://www.npmjs.com/package/@types/selectn) [](https://www.npmjs.org/package/selectn) [](https://www.npmjs.org/package/selectn)
```shell
yarn add selectn
```
or
```shell
npm install selectn --save
```
or
```html
<script src="https://unpkg.com/selectn/selectn.min.js"></script>
```
> You may also install `selectn` via [Bower] or [jspm].
## Overview
###### allows you to refactor this:
person && person.info && person.info.name && person.info.name.full
###### into:
selectn('info.name.full', person)
###### or refactor this:
contacts.map(function (contact) {
return contact && contact.addresses && contact.addresses[0]
})
###### into:
contacts.map(selectn('addresses[0]')))
## Demo
[](https://www.npmjs.org/package/selectn)
## Features
- Mitigate boilerplate guards like `if (obj && obj.a && obj.a.b && obj.a.b.c)`.
- Mitigate **TypeError** `Cannot read property '...' of undefined`.
- Multiple levels of array nesting: `'group[0].section.a.seat[3]'`.
- Dashed key access: `'stats.temperature-today'`.
- When the value at the given path is a function, it is invoked and the functions returned value is returned.
- `selectn` is auto-curried so [partial application is automatic][Un-bind your JS with curry] when you omit the second argument.
- `selectn` uses Haskell style [parameter order] (AKA: data-last) which enables [pointfree style programming][Un-bind your JS with curry].
- Functions returned by `selectn` are higher-order property accessors which can be passed to other higher-order functions like [map] or [filter].
- Compatible with [modern and legacy browsers][browsers], Node/CommonJS, and AMD.
## Non-Features
- No [eval][] or [Function][] (see: [`eval`][note] in disguise).
- No [typeof][] since, [typeof][] is not a real solution to this problem but can _appear_ to be due to the way the global scope is _implied_.
## Usage example(s)
#### property accessor as predicate
> Avoid annoying __Cannot read property '...' of undefined__ `TypeError` without writing boilerplate anonymous functions or guards.
```js
var selectn = require('selectn')
var language = [
{ strings: { en: { name: 'english' } }},
{ strings: { es: { name: 'spanish' } }},
{ strings: { km: { name: 'khmer' } }},
{ strings: { es: { name: 'spanish' } }},
{ nodatas: {}}
]
var spanish = selectn('strings.es')
//=> [Function]
language.filter(spanish).length
//=> 2
```
#### point-free property accessor
> Access deeply nested properties (including dashed properties) using point-free style.
```js
var selectn = require('selectn')
var data = {
client: {
message: { 'message-id': 'd50afb80-a6be-11e2-9e96-0800200c9a66' }
}
}
var getId = selectn('client.message.message-id')
//=> [Function]
Promise.resolve(data).then(getId)
//=> 'd50afb80-a6be-11e2-9e96-0800200c9a66'
```
#### property accessor for functor
> Avoid wrapping property accessors in anonymous functions.
```js
var selectn = require('selectn')
var contacts = [
{ addresses: [ '123 Main St, Broomfield, CO 80020', '123 Main St, Denver, CO 80202' ] },
{ addresses: [ '123 Main St, Kirkland, IL 60146' ] },
{ phones: [] },
]
var primaryAddress = selectn('addresses[0]')
//=> [Function]
contacts.map(primaryAddress)
//=> [ '123 Main St, Broomfield, CO 80020', '123 Main St, Kirkland, IL 60146', undefined ]
```
#### support for keys containing `.`
> Pass an array as path instead of a string.
```js
var selectn = require('selectn')
var data = {
client: {
'message.id': 'd50afb80-a6be-11e2-9e96-0800200c9a66'
}
}
selectn(['client', 'message.id'], data)
//=> 'd50afb80-a6be-11e2-9e96-0800200c9a66'
```
#### value at path is a function
> Avoid `var fn = data.may.be.a.fn; if (typeof fn === 'function') fn()`.
```js
var selectn = require('selectn')
function hi () { return 'hi' }
var data = { may: { be: { a: { fn: hi } } } }
selectn('may.be.a.fn', data)
//=> 'hi'
```
## API (partial application)
### `selectn(String|Array)`
###### arguments
* `path (String|Array)` Dot/bracket-notation string path or array.
###### returns
- `(Function)` Unary function accepting the object to access.
## API (full application)
### `selectn(String|Array, Object)`
###### arguments
* `path (String|Array)` Dot/bracket-notation string path or array.
* `object (String|Array)` Object to access.
###### returns
- `(*|undefined)` Value at path if path exists or `undefined` if path does not exist.
## Other Languages
`selectn` has inspired ports to other languages:
|Language|Project|
|---|---|
|Python|[selectn](https://pypi.python.org/pypi/selectn)|
## Related
Other JS packages whose friendly API is driven by `selectn`:
- [array-groupby]
- [array.filter]
- [arraymap]
- [orderby-time]
- [regexp-id]
- [sum.js]
## Inspiration
JS packages that have inspired `selectn`:
- [reach]
- [to-function]
## Alternatives
Alternative packages you might like instead of `selectn`:
- [_.get]
- [dref]
- [path-lookup]
- [pathval]
- [reach]
- [to-function]
## Licenses
[](license)
[_.get]: https://www.npmjs.com/package/lodash.get
[Bower]: http://bower.io
[Duo]: http://duojs.org
[Function]: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function
[Property accessors]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
[Sauce Test Status]: https://saucelabs.com/browser-matrix/selectn.svg
[Un-bind your JS with curry]: https://medium.com/@wilmoore/un-bind-your-js-with-curry-a8657a4138cb#.6dswguc2q
[array-groupby]: https://www.npmjs.com/package/array-groupby
[array.filter]: https://www.npmjs.com/package/array.filter
[arraymap]: https://www.npmjs.com/package/arraymap
[browsers]: https://saucelabs.com/u/selectn
[dref]: https://github.com/crcn/dref.js
[eval]: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval
[filter]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
[jspm]: http://jspm.io
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
[note]: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Member_Operators#Note_on_eval
[orderby-time]: https://www.npmjs.com/package/orderby-time
[parameter order]: https://wiki.haskell.org/Parameter_order
[path-lookup]: https://github.com/yields/path-lookup
[pathval]: https://www.npmjs.com/package/pathval
[reach]: https://github.com/spumko/hoek#reachobj-chain
[regexp-id]: https://www.npmjs.com/package/regexp-id
[sum.js]: https://www.npmjs.com/package/sum.js
[to-function]: https://github.com/component/to-function
[typeof]: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/typeof