diff --git a/README.md b/README.md index 8ef8c524c..9d52eef87 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/src/Maybe/prop.js b/src/Maybe/prop.js index 9b6c8f8bb..3aa24f326 100644 --- a/src/Maybe/prop.js +++ b/src/Maybe/prop.js @@ -3,12 +3,13 @@ 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) { @@ -16,6 +17,10 @@ function prop(key, target) { throw new TypeError('prop: String or integer required for first argument') } + if(isNil(target)) { + return Nothing() + } + return lift(target[key]) } diff --git a/src/Maybe/prop.spec.js b/src/Maybe/prop.spec.js index c9a1d1e4e..b6c06b811 100644 --- a/src/Maybe/prop.spec.js +++ b/src/Maybe/prop.spec.js @@ -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() }) diff --git a/src/Maybe/propPath.js b/src/Maybe/propPath.js index 4f3ff2c43..f0afe62ee 100644 --- a/src/Maybe/propPath.js +++ b/src/Maybe/propPath.js @@ -2,15 +2,17 @@ /** @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) { @@ -18,6 +20,9 @@ function propPath(keys, target) { 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') diff --git a/src/Maybe/propPath.spec.js b/src/Maybe/propPath.spec.js index 0722397ef..402d5b239 100644 --- a/src/Maybe/propPath.spec.js +++ b/src/Maybe/propPath.spec.js @@ -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() }) diff --git a/src/Maybe/safe.js b/src/Maybe/safe.js index 7e9cff401..45176e9b3 100644 --- a/src/Maybe/safe.js +++ b/src/Maybe/safe.js @@ -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') @@ -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) diff --git a/src/Maybe/safe.spec.js b/src/Maybe/safe.spec.js index 53b1e4a8d..40394fe75 100644 --- a/src/Maybe/safe.spec.js +++ b/src/Maybe/safe.spec.js @@ -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() +}) diff --git a/src/Maybe/safeLift.js b/src/Maybe/safeLift.js index 732f946ef..54ef1d925 100644 --- a/src/Maybe/safeLift.js +++ b/src/Maybe/safeLift.js @@ -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) diff --git a/src/core/safe.js b/src/core/safe.js deleted file mode 100644 index c0416f50b..000000000 --- a/src/core/safe.js +++ /dev/null @@ -1,13 +0,0 @@ -/** @license ISC License (c) copyright 2016 original and current authors */ -/** @author Ian Hofmann-Hicks (evil) */ - -const { Nothing, Just } = require('./Maybe') -const predOrFunc = require('./predOrFunc') - -// safe : ((a -> Boolean) | Pred) -> a -> Maybe a -function safe(pred) { - return x => - predOrFunc(pred, x) ? Just(x) : Nothing() -} - -module.exports = safe diff --git a/src/core/safe.spec.js b/src/core/safe.spec.js deleted file mode 100644 index 4b5ee99a6..000000000 --- a/src/core/safe.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const test = require('tape') - -const Pred = require('../Pred') -const isFunction = require('./isFunction') - -const safe = require('./safe') - -test('safe core', t => { - t.ok(isFunction(safe), 'is a function') - 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() -})