rambda: defaultsTo shouldn't factor in type

As far as I can tell defaultsTo(defaultArgument, inputArgument) is the only method that provides a way for defaulting to a value (providing some ‘or’ logic). However, the built-in test for inputArgument being the same type as defaultArgument limits the use of this function. It is my opinion that defaultArgument should only be returned if inputArgument is undefined.

I would like to compose a function works the same as Lodash’s get method or Ramda’s pathOr method whereby a value is read from a path through a nested object tree and defaults to a value if this path returns undefined.

import { get } from 'lodash'
import { pathOr } from 'ramda'
import { curry, defaultsTo, path } from 'rambda'

// Tests written with jest

test('lodash get', () => {
  expect(get({ a: { b: 2 } }, 'a.b')).toBe(2)
  expect(get({ c: { b: 2 } }, 'a.b')).toBeUndefined()
  expect(get({ c: { b: 2 } }, 'a.b', 'N/A')).toBe('N/A')
})

test('ramda pathOr', () => {
  // From ramda's docs http://ramdajs.com/docs/#pathOr
  expect(pathOr('N/A', ['a', 'b'], { a: { b: 2 } })).toBe(2)
  expect(pathOr('N/A', ['a', 'b'], { c: { b: 2 } })).toBe('N/A')
})

// There's probably a more elegant way of doing this?
const rambdaPathOr = curry((defaultValue, inputPath, inputValue) =>
  defaultTo(defaultValue, path(inputPath, inputValue)))

test('rambda pathOr', () => {
  // This first test fails because the value of a.b is 2 (type 'Number')
  // and is a different type to the default value 'N/A' (type 'String')
  expect(rambdaPathOr('N/A', ['a', 'b'], { a: { b: 2 } })).toBe(2)
  expect(rambdaPathOr('N/A', ['a', 'b'], { c: { b: 2 } })).toBe('N/A')
})

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

😃 I was thinking the whole time that typed will be better. So we’ll do that then. As I mentioned earlier I will add the methods before the end of the day.

Ignore what I posted above re. null and NaN not being counted as value types for defaulting to a value…I now see where you got you logic for defaultTo from: ramda. Apologies!

http://ramdajs.com/docs/#defaultTo

I also see where the name comes from, so also ignore my suggestion to rename defaultTo to defaultOr. You should stick as close to ramda as possible IMO 😃

Fair enough re. defaultTo—I can certainly appreciate it’s use case.

Reason I posted this issue is because it is a common design pattern for an options object to be passed to a function/constructor where certain key values can have multiple types (typically 2).

One such example might be a key value on an options object that can either be a function or a falsy value like false or null.

I have been working on a package called ig-api that uses rambda (thank you and kudos btw, it’s really nice). I make use of an options object that has a couple of key values that can either be transform functions or set to false to prevent any transformations from happening. You can read the docs here.

By default, some built-in transform functions are used unless the user specifies their own or chooses to disable them by passing false. In this use case, the default value is typically a function, but a value of false can be provided and is totally valid. With defaultsTo this does not work since false is obviously not the same type as the default function value.

Anyway, I digress. With regards to pathOr, I think it would be better if you could implement this please. While my implementation below certainly works and passes the tests, I expect there might be a better way of writing it using rambda’s internals? If not, then please go ahead and simple add my implementation:

const pathOr = curry((defaultValue, inputPath, inputObject) => {
  const inputValue = path(inputPath, inputObject)
  return type(inputValue) === 'Undefined' ? defaultValue : inputValue
})