rjhilgefort
10/20/2019 - 7:47 PM

evolveSpec

import { isArray, curry, isPlainObject, isFunction, isNil } from 'lodash/fp'
import { mapValuesWithKey } from './mapValuesWithKey'
import { mapWithKey } from './mapWithKey'

const iteratee = (spec, full) => (val, key) => fn(spec[key], val, full)

const fn = (spec, data, full) => {
  if (isNil(spec)) return data
  if (isFunction(spec)) return spec(full)
  if (isPlainObject(spec)) return mapValuesWithKey(iteratee(spec, full), data)
  if (isArray(spec)) return mapWithKey(iteratee(spec, full), data)
  return data
}

/**
 * @name evolveSpec
 *
 * @description
 *   Like `evolve`, but useful when you need to modify a prop on an object/array,
 *   but need the entire object/array context (instead of just the prop value).
 *
 * @see ramda.evolve
 *
 * @example
 *   evolveSpec(
 *     { name: ({ _key, name }) => `${name} (${_key})` },
 *     { _key: 'k_1', name: 'Some name' },
 *   ) // -> { _key: 'k_1', name: 'Some name (k_1)' }
 *
 * @signature
 *   { [String]: a -> Any } -> Object a -> Object b
 */
export const evolveSpec = curry((spec, data) => fn(spec, data, data))
import { evolveSpec } from './'

it.each([
  ['`null` spec', null, { foo: 'foo' }, { foo: 'foo' }],
  [
    'simple object',
    { name: ({ _key, name }) => `${name} (${_key})` },
    { _key: 'k_1', name: 'Some name' },
    { _key: 'k_1', name: 'Some name (k_1)' },
  ],
  [
    'nested object',
    { foo: { name: ({ foo: { _key, name } }) => `${name} (${_key})` } },
    { foo: { _key: 'k_1', name: 'Some name' }, bar: 5 },
    { foo: { _key: 'k_1', name: 'Some name (k_1)' }, bar: 5 },
  ],
  [
    'simple array',
    [null, ([_key, name]) => `${name} (${_key})`],
    ['k_1', 'Some name'],
    ['k_1', 'Some name (k_1)'],
  ],
  [
    // Probably don't do this, but it's supported
    'nested array',
    [null, [null, ([, [_key, name]]) => `${name} (${_key})`]],
    [5, ['k_1', 'Some name'], 6],
    [5, ['k_1', 'Some name (k_1)'], 6],
  ],
])('%p', (_label, spec, data, expected) => {
  expect(evolveSpec(spec, data)).toEqual(expected)
})