Skip to content

Latest commit

 

History

History
295 lines (225 loc) · 7.06 KB

README.md

File metadata and controls

295 lines (225 loc) · 7.06 KB

ramda-sheep

𝑓(🐏) = 🐑, A set of useful functions built on top of Ramda that are not in the core.

Function are defined in a readme for now.

If you are looking for more advanced set of extensions look at ramda-adjunct

get

String → {s: a} → a

Just like lodash get accepts . delimeted path instead of an array:

const pathGetter = R.compose(R.path, R.split('.'))
const get = R.curry((path, obj) => pathGetter(path)(obj))

usage:

get('a.b', {a: {b: 8}}) //8

set

String → a → {a} → {a}

Sets a property on an object using given path, returns new object.

const pathSetter = R.compose(R.assocPath, R.split('.'))
const set = R.curry((path, value, obj) => pathSetter(path)(value, obj))

usage:

set('a.b.c', 8, {}) // {a: {b: {c: 8}}}

isNilOrEmpty

Obj → Boolean

Checks if value is nil or empty.

const isNilOrEmpty = R.either(R.isNil, R.isEmpty)

differenceAllWith(predicate, listOfLists)

Same as differenceWith, but for multiple lists

  const differenceAllWith = R.curry((predicate, array) =>
    R.reduce(
      R.differenceWith(predicate),
      R.head(array),
      R.tail(array)
  ))

strReplaceAll(search, replacement, target)

Replaces all occurences of a string with given replacement string.

const strReplaceAll = R.curry((search, replacement, str) =>
	R.replace(new RegExp(search, 'g'), replacement, str),
)

usage:

R.pipe(
  strReplaceAll('&', 'ramda'), 
  strReplaceAll('x', 'is'),
  strReplaceAll('y', 'cool'),
)('& x y')
// outputs "ramda is cool"

replaceBy(predicate, replaceWithItems, originalItems)

((originalItem, itemToReplaceWith) → Boolean) → Array → Array → Array

Replaces original items with given array of items using predicate, if predicate returns true, item will be replaced in original items in-place (it's orignal index) with the item from "replaceWithItems".

  const replaceBy = R.curry((predicate, replaceWithItems, originalItems) =>
    R.compose(
      R.reduce(
        (acc, itemsMap) =>
          R.update(itemsMap.index, itemsMap.item, acc),
        originalItems
      ),
      R.map(replacementItem => ({
        item: replacementItem,
        index: R.findIndex(
          originalItem => predicate(originalItem, replacementItem),
          originalItems
        )
      }))
    )(replaceWithItems))

usage:

replaceBy((original, updated) => original.id === updated.id, updatedUsers, allUsers)

updateBy(predicate, item, target)

Updates first item found using given predicate with a new item. To update multiple items use replaceBy defined above.

const updateBy = R.curry((func, value, target) => {
  const index = R.findIndex(func, target);
  return index >= 0 ? R.update(index, value, target) : target;
});

usage:

updateBy(x => x === 1, 'hi', [0,1,2,3]) //? [ 0, 'hi', 2, 3 ] 

pluckPath(strPath, list)

Like R.pluck, but with string path as a first argument. R.pluck allows to specify property name, while pluckPath can work with propert pathes like myProp.otherProp.nestedProp

const pluckPath = R.pipe(
  R.split('.'),
  R.path,
  R.map,
)
const pluckPathCurr = R.curry((path, obj) => pluckPath(path)(obj))

usage:

const list = [
  { x: { y: 1} },
  { x: { y: 2} },
  { x: { y: 3} },
]

pluckPath('x.y')(list) // [1, 2, 3]

indexByWith

Like R.indexBy, but accepts additional tranformation function to transform value for the given key.

const R = require('ramda')

const indexByWith = R.curry((keygenFunc, fn, items) =>
  R.reduce(
    (acc, item) => {
      acc[keygenFunc(item)] = fn(item)
      return acc
    },
    {},
    items,
  ),
)

usage:

indexByWith(R.prop('foo'), item => ({ a: item }), [
  { foo: 1 },
  { foo: 2 },
  { foo: 3 },
])

reduceObj

Like R.reduce, but for objects.

const reduceObj = R.useWith(R.reduce, [R.identity, R.identity, R.toPairs]);

usage:

reduceObj((acc, [key, val]) => return acc, {}, obj)

transformObjectDeep

Recursively traverse object and apply transformer function to every [key, value] pair and form new object based on that. If transformation function returns undefined then those [key, value] paris get removed from the original object. Keeps arrays as-is, meaning object inside an array won't be transformed.

const reduceObj = R.useWith(R.reduce, [R.identity, R.identity, R.toPairs]);

// given key value pair return new key value pair or nothing
const removeEmptyKeysTransformer = ([key, val]) => {
  if (val === undefined) {
    return undefined;
  }
  return [key, val];
};

// transformer is a function that takes [key, value] and return transformed pair or undefined
// if undefined is returned that pair is going to be removed from the object. Recursively applied
// properties of the object. Keeps arrays as-is, meaning object inside an array won't be transformed.
// transformer is a function that takes [key, value] and return transformed pair or undefined
// if undefined is returned that pair is going to be removed from the object. Recursively applied
// properties of the object. Keeps arrays as-is, meaning object inside an array won't be transformed.
// Handles circular references. 
const transformObjectDeep = R.curry((transformer, obj, objectCache = undefined) => {
  const cache = objectCache || new Set();
  cache.add(obj);

  return reduceObj(
    (acc, [key, val]) => {
      if (!Array.isArray(val) && val !== null && typeof val === 'object') {
        // detecting circular references
        if (cache.has(val)) {
          log.warn(`🌀 Circular reference detected in "transformObjectDeep", skipping, key=${key}`);
          acc[key] = `[Circular]`;
          return acc;
        }

        cache.add(val);

        const transformedVal = transformObjectDeep(transformer, val, cache);

        acc[key] = transformedVal;
        return acc;
      }

      const transformedKeyVal = transformer([key, val]);

      if (transformedKeyVal === undefined) {
        return acc;
      }

      const [tKey, tVal] = transformedKeyVal;
      acc[tKey] = tVal;

      return acc;
    },
    {},
    obj,
  );
});

usage:

// given key value pair return new key value pair or nothing
const removeEmptyKeysTransformer = ([key, val]) => {
  if (val === undefined) {
    return undefined;
  }
  return [key, val];
};

transformObjectDeep(removeEmptyKeysTransformer, { x: 8, y: undefined } ); //output: { x: 8 }

findMaxBy

Func → [a] → a

Find element in a list that has max value specified in the provided path using path getter function.

const findMaxBy = R.curry((path, arr) =>
  R.pipe(R.reduce((acc, x) => (path(acc) > path(x) ? acc : x), {}))(arr),
)

usage:

const prs = [
	{ pullRequests: { totalCount: 10 } },
	{ pullRequests: { totalCount: 8 } },
	{ pullRequests: { totalCount: 20 } },
	{ pullRequests: { totalCount: 12 } },
]

findMaxBy(R.path(['pullRequests', 'totalCount']), prs) // outputs: { pullRequests: { totalCount: 20 } } 

Curated List of Libraries

Partial Lenses