Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow prop and propPath to accept null and undefined as data #145

Merged
merged 1 commit into from
Sep 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,14 @@ flow('string', 100).runWith(data)
#### prop
`crocks/Maybe/prop`
```haskell
prop : (String | Number) -> (Object | Array) -> Maybe a
prop : (String | Number) -> a -> Maybe b
```
If you want some safety around pulling a value out of an Object or Array with a single key or index, you can always reach for `prop`. Well, as long as you are working with non-nested data that is. Just tell `prop` either the key or index you are interested in, and you will get back a function that will take anything and return a `Just` with the wrapped value if the key/index exists. If the key/index does not exist however, you will get back a `Nothing`.

#### propPath
`crocks/Maybe/propPath`
```haskell
propPath : [ String | Number ] -> (Object | Array) -> Maybe a
propPath : [ String | Number ] -> a -> Maybe b
```
While [`prop`](#prop) is good for simple, single-level structures, there may come a time when you have to work with nested POJOs or Arrays. When you run into this situation, just pull in `propPath` and pass it a left-to-right traversal path of keys, indices or a combination of both (gross...but possible). This will kick you back a function that behaves just like [`prop`](#prop). You pass it some data, and it will attempt to resolve your provided path. If the path is valid, it will return the value residing there (`null` included!) in a `Just`. But if at any point that path "breaks" it will give you back a `Nothing`.

Expand Down
11 changes: 8 additions & 3 deletions src/Maybe/prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@

const curry = require('../core/curry')
const isDefined = require('../core/isDefined')
const isNil= require('../core/isNil')
const isInteger = require('../core/isInteger')
const isString = require('../core/isString')
const safe = require('../core/safe')
const { Nothing, Just } = require('../core/Maybe')

const lift =
safe(isDefined)
const lift = x =>
isDefined(x) ? Just(x) : Nothing()

// prop : String | Number -> a -> Maybe b
function prop(key, target) {
if(!(isString(key) || isInteger(key))) {
throw new TypeError('prop: String or integer required for first argument')
}

if(isNil(target)) {
return Nothing()
}

return lift(target[key])
}

Expand Down
6 changes: 6 additions & 0 deletions src/Maybe/prop.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,11 @@ test('prop function', t => {
t.equals(arrBad.option('nothing'), 'nothing', 'returns a Nothing when index is not found')
t.equals(arrNull(arr).option('nothing'), null, 'returns a Just null when index is found and value is null')

const fn =
x => prop('key', x).option('nothing')

t.equals(fn(undefined), 'nothing', 'returns Nothing when data is undefined')
t.equals(fn(null), 'nothing', 'returns Nothing when data is null')

t.end()
})
13 changes: 9 additions & 4 deletions src/Maybe/propPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@
/** @author Ian Hofmann-Hicks (evil) */

const Maybe = require('../core/Maybe')
const { Nothing, Just } = Maybe

const curry = require('../core/curry')
const isArray = require('../core/isArray')
const isDefined = require('../core/isDefined')
const isInteger = require('../core/isInteger')
const isNil= require('../core/isNil')
const isString = require('../core/isString')
const isArray = require('../core/isArray')
const safe = require('../core/safe')

const lift =
safe(isDefined)
const lift = x =>
isDefined(x) ? Just(x) : Nothing()

// propPath : [ String | Number ] -> a -> Maybe b
function propPath(keys, target) {
if(!isArray(keys)) {
throw new TypeError('propPath: Array of strings or integers required for first argument')
}

if(isNil(target)) {
return Nothing()
}
return keys.reduce((maybe, key) => {
if(!(isString(key) || isInteger(key))) {
throw new TypeError('propPath: Array of strings or integers required for first argument')
Expand Down
6 changes: 6 additions & 0 deletions src/Maybe/propPath.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,11 @@ test('propPath function', t => {

t.equals(propPath([ 'things', 2 ], mixed).option('nothing'), value, 'allows for traversal with a mixed path on a mixed structure')

const fn =
x => propPath([ 'key' ], x).option('nothing')

t.equals(fn(undefined), 'nothing', 'returns Nothing when data is undefined')
t.equals(fn(null), 'nothing', 'returns Nothing when data is null')

t.end()
})
7 changes: 5 additions & 2 deletions src/Maybe/safe.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/** @license ISC License (c) copyright 2016 original and current authors */
/** @author Ian Hofmann-Hicks (evil) */

const _safe = require('../core/safe')
const Pred = require('../core/types').proxy('Pred')
const { Nothing, Just } = require('../core/Maybe')
const predOrFunc = require('../core/predOrFunc')

const curry = require('../core/curry')
const isFunction = require('../core/isFunction')
Expand All @@ -14,7 +15,9 @@ function safe(pred, x) {
throw new TypeError('safe: Pred or predicate function required for first argument')
}

return _safe(pred)(x)
return predOrFunc(pred, x)
? Just(x)
: Nothing()
}

module.exports = curry(safe)
28 changes: 28 additions & 0 deletions src/Maybe/safe.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,31 @@ test('safe helper', t => {

t.end()
})

test('safe predicate function', t => {
const pred = x => !!x

const f = safe(pred)

const fResult = f(false).option('nothing')
const tResult = f('just').option('nothing')

t.equals(fResult, 'nothing', 'returns a Nothing when false')
t.equals(tResult, 'just', 'returns a Just when true')

t.end()
})

test('safe Pred', t => {
const pred = Pred(x => !!x)

const f = safe(pred)

const fResult = f(0).option('nothing')
const tResult = f('just').option('nothing')

t.equals(fResult, 'nothing', 'returns a Nothing when false')
t.equals(tResult, 'just', 'returns a Just when true')

t.end()
})
2 changes: 1 addition & 1 deletion src/Maybe/safeLift.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const compose = require('../core/compose')
const curry = require('../core/curry')
const isFunction = require('../core/isFunction')
const isSameType = require('../core/isSameType')
const safe = require('../core/safe')
const safe = require('./safe')

const map =
fn => m => m.map(fn)
Expand Down
13 changes: 0 additions & 13 deletions src/core/safe.js

This file was deleted.

39 changes: 0 additions & 39 deletions src/core/safe.spec.js

This file was deleted.