npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lodash-fp-ex

v1.1.37

Published

lodash/fp extensions

Downloads

89

Readme

lodash-fp-ex

Overview

functions to add in lodash.mixin

Install

$npm i lodash lodash-fp-ex

Usage

import fp from 'lodash/fp';
import lodashFpEx from 'lodash-fp-ex';

fp.mixin(lodashFpEx);

APIs

All functions are curried except promisify.

mapASync, filterAsync, reduceAsync, findAsync, forEachAsync, promisify, andThen, otherwise, finally, isPromise, isNotEmpty, isNotNil, isJson, notEquals(isNotEqual), isVal(isPrimitive), isRef(isReference), not, notIncludes, toBool, deepFreeze, key(keyByVal), transformObjectKey, toCamelcase(toCamelKey), toSnakecase(toSnakeKey), pascalCase, isDatetimeString, ap, instanceOf, removeByIndex(removeByIdx), removeLast, append, prepend, mapWithKey(mapWithIdx, mapWithIndex), forEachWithKey(forEachWithIdx, forEachWithIndex), reduceWithKey(reduceWithIdx, reduceWithIndex), isFalsy, isTruthy, delayAsync(sleep)

mapAsync

mapAsync works with Promise.all

type MapAsync = F.Curry<
  <T, K extends keyof T, R>(
    asyncMapper: (arg: T[K], key: K) => Promise<R>,
    collection: T,
  ) => Promise<R[]>
>;
(async () => {
  const arr = [1, 2, 3, 4, 5];
  const obj = { a: 1, b: 2, c: 3 };
  const asyncMapper = (a) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(2 * a);
      }, 5);
    });

  // it takes about 5ms + alpha.
  const results = await fp.mapAsync(asyncMapper, arr);
  // => [2, 4, 6, 8, 10]
  const results1 = await fp.mapAsync(asyncMapper, obj);
  // => [2, 4, 6]
})();

filterAsync

filterAsync works with Promise.all

type FilterAsync = F.Curry<
  <T, K extends keyof T, R>(
    asyncFilter: (arg: T[K], key: K) => Promise<boolean>,
    collection: T,
  ) => Promise<R[]>
>;
(async () => {
  const arr = [1, 2, 3, 4, 5];
  const asyncFilter = (a) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(!fp.equals(0, a % 2));
      }, 5);
    });

  // => it takes about 5ms + alpha.
  const results = await fp.filterAsync(asyncFilter, arr);
  // => [1,3,5]
})();

reduceAsync

reduceAsync works different way from mapAsync and filterAsync, it works with Promise.resolve. So if you more important function order than performance, reduceAsync is suitable.

type ReduceAsync = F.Curry<
  <T, K extends keyof T>(
    asyncFn: (acc: any, arg: T[K], key: K) => Promise<any>,
    initAcc: Promise<any> | any,
    collection: T,
  ) => Promise<any>
>;
(async () => {
  const asyncMapper = (a) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(2 * a);
      }, 5);
    });

  // it takes about (5 * array length)ms + alpha.
  const results = await fp.reduceAsync(
    async (accP, v) => {
      const acc = await accP; // you should await acc first.
      const nextVal = await asyncMapper(v);
      acc.push(nextVal);

      return acc;
    },
    [],
    arr,
  );
  // => [2, 4, 6, 8, 10]
})();

findAsync

type FindAsync = F.Curry<
  <T, K extends keyof T, R>(
    asyncFilter: (arg: T[K], key: K) => Promise<boolean>,
    collection: T,
  ) => Promise<R>
>;
(async () => {
  const arr = [
    { name: 'hi', age: 21 },
    { name: 'hello', age: 22 },
    { name: 'alo', age: 23 },
  ];
  const asyncFilter = (a) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(fp.pipe(fp.get('name'), fp.equals('hello'))(a));
      }, 5);
    });

  const result = await fp.findAsync(asyncFilter, arr);
  console.log(result);
  // => { name: 'hello', age: 22 }
})();

forEachAsync

type ForEachAsync = F.Curry<
  <T, K extends keyof T, R>(
    callbackAsync: (value: T[K], key: K) => Promise<R>,
    collection: T,
  ) => Promise<R[]>
>;
(async () => {
  const asyncMapper = (v, i) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(i > 0 ? v * i : v);
      }, 5);
    });

  const results = await fp.forEachAsync(
    async (v, i) => {
      const nextVal = await asyncMapper(v, i);
      return nextVal;
    },
    [1, 2, 3, 4, 5],
  );
  // => [1, 2, 6, 12, 20]
})();

(async () => {
  const asyncMapper1 = (v, k) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(`${v} ${k}`);
      });
    }, 5);

  const results1 = await fp.forEachAsync(
    async (v, k) => {
      const nextVal = await asyncMapper1(v, k);
      return nextVal;
    },
    {
      key: 'val',
      hello: 'world',
      'led zeppelin': 'stairway to heaven',
    },
  );
  // => ['val key', 'world hello', 'stairway to heaven led zeppelin']
})();

promisify

wrap argument with Promise
Note: Promisify is not curried to accept Function on first argument. Only when first argument is function, other arguments can be applied.

type Promisify = (a: any, ...args: any[]): Promise<any>
(async () => {
  const result = await fp.promisify(128);
  // => 128
  const result1 = await fp.promisify((a, b) => a + b, 64, 64);
  // => 128
  const result2 = await fp.promisify(Promise.resolve(128));
  // => 128
})();

andThen

alias: andThen

Make Promise.then work with \fp.pipe

type Then = F.Curry<
  (fn: (response: any) => any, thenable: Promise<any>) => Promise<any>
>;
(async () => {
  const p = (a) =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(2 * a);
      }, 5);
    });

  const composer = fp.pipe(p, fp.andThen(fp.identity));
  const result1 = await composer(64);
  // => 128
  const result2 = await fp.andThen(fp.identity, p(64));
  // => 128
  const result3 = await fp.pipe(
    p,
    fp.andThen((x) => x / 2),
  )(128);
  // => 128
})();

otherwise

Make Promise.catch work with fp.pipe.

type Totherwise = F.Curry<
  (
    failureHandler: (error: Error | any) => never | any,
    thenable: Promise<Error | any>,
  ) => Promise<never | any>
>;
(async () => {
  const p = (a) =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        if (fp.equals(a * a, a)) {
          resolve(a);
        } else {
          reject(new Error('wrong'));
        }
      });
    });
  const composer = fp.pipe(p, fp.andThen(fp.identity), fp.catch(fp.identity));
  const result1 = await composer(1);
  // => 1
  const result2 = await composer(2);
  // => error 'wrong'
})();

finally

Make Promise.finally work with \fp.pipe.

type Finally = F.Curry<
  (callback: (...args: any[]) => any, thenable: Promise<any>) => Promise<any>
>;
(async () => {
  let isLoading = true;
  const p = (a) =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        if (fp.equals(a * a, a)) {
          resolve(a);
        } else {
          reject(new Error('wrong'));
        }
      });
    });
  const composer = fp.pipe(
    p,
    fp.andThen(fp.identity),
    fp.catch(fp.identity),
    fp.finally(() => (isLoading = false)),
  );

  await composer(1);
  // => false
})();

isPromise

Check argument is promise.

type IsPromise = <T>(x: T): boolean
(() => {
  const p = Promise.resolve(1);
  const fn = () => 1;
  const str = '1';
  const num = 1;

  fp.isPromise(p);
  // => true
  fp.isPromise(fn);
  // => false
  fp.isPromise(str);
  // => false
  fp.isPromise(num);
  // => false
  fp.isPromise(null);
  // => false
  fp.isPromise(undefined);
  // => false
})();

isNotEmpty

opposite of lodash.isEmpty

type IsNotEmpty = (a: any) => boolean;
(() => {
  fp.isNotEmpty([]);
  // => false
  fp.isNotEmpty({});
  // => false
  fp.isNotEmpty(1);
  // => false
  fp.isNotEmpty(''));
  // => false
  fp.isNotEmpty('str');
  // => true
  fp.isNotEmpty(null);
  // => false
  fp.isNotEmpty(undefined);
  // => false
})();

isNotNil

opposite of lodash.isNil

type IsNotNil = (arg: any) => boolean;
(() => {
  fp.isNotNil(null);
  // => false
  fp.isNotNil(undefined);
  // => false
  fp.isNotNil(1);
  // => true
  fp.isNotNil({});
  // => true
  fp.isNotNil(() => {});
  // => true
})();

isJson

Check argument is json string.

type IsJson = (arg: any) => boolean;
(() => {
  fp.isJson('{ "test": "value" }');
  // => true
  fp.isJson('test');
  // => false
  fp.isJson({ test: 'value' });
  // => false
})();

notEquals

alias: isNotEqual

opposite of lodash.isEqual

type NotEquals = F.Curry<(a: any, b: any) => boolean>;
(() => {
  fp.notEquals({ a: 1 }, { a: 1 });
  // => false
  fp.notEquals([1, 2, 3], [1, 2, 3]);
  // => false
  fp.notEquals([1, 2, 3], [2, 3, 4]);
  // => true
  fp.notEquals('string', 'number');
  // => true
  fp.notEquals(1, 2);
  // => true
})();

isVal

alias: isPrimitive

Check agument is primitive type.

type IsVal = (arg: any) => boolean;
(() => {
  fp.isVal(null);
  // => true
  fp.isVal(undefined);
  // => true
  fp.isVal(false);
  // => true
  fp.isVal(1);
  // => true
  fp.isVal('string');
  // => true

  fp.isVal([]);
  // => false
  fp.isVal({});
  // => false
  fp.isVal(() => {});
  // => false
})();

isRef

alias: isReference

Check agument is reference type.

type IsRef = (arg: any) => boolean;
(() => {
  fp.isRef(null);
  // => false
  fp.isRef(undefined);
  // => false
  fp.isRef(false);
  // => false
  fp.isRef(1);
  // => false
  fp.isRef('string');
  // => false

  fp.isRef([]);
  // => true
  fp.isRef({});
  // => true
  fp.isRef(() => {});
  // => true
})();

not

Apply ! operator to argument.

type Not = <T>(a: T) => boolean;
(() => {
  fp.not(false);
  // => true
  fp.not(0);
  // => true

  fp.not('string');
  // => false
  fp.not(true);
  // => false
  fp.not(1);
  // => false
  fp.not({});
  // => false
})();

notIncludes

Opposite of lodash.includes

type NotIncludes = F.Curry<
  (arg: any, targetArray: any[] | Record<string, any> | string) => boolean
>;
(() => {
  fp.notIncludes(1, [1, 2, 3]);
  // => false
  fp.notIncludes('s', 'string');
  // => false
  fp.notIncludes(1, { a: 1, b: 2 });
  // => false
})();

toBool

'true', 'false' string and other argument convert to Boolean type.

type ToBool = (arg: any) => boolean;
(() => {
  fp.toBool(1);
  // => true

  fp.toBool(0);
  // => false
  fp.toBool(null);
  // => false
  fp.toBool(undefined);
  // => false
})();

deepFreeze

Reference type target freeze deeply.

type DeepFreeze = (obj: Record<string, any>) => Record<string, any>;
(() => {
  const shallowFrozen = Object.freeze({
    a: {
      b: [],
    },
  });
  const deepFrozen = fp.deepFreeze({
    a: {
      b: [],
      c: () => {},
    },
  });

  Object.isFrozen(shallowFrozen);
  // => true
  Object.isFrozen(shallowFrozen.a);
  // => false
  Object.isFrozen(shallowFrozen.a.b);
  // => false

  Object.isFrozen(deepFrozen);
  // => true
  Object.isFrozen(deepFrozen.a);
  // => true
  Object.isFrozen(deepFrozen.a.b);
  // => true
  Object.isFrozen(deepFrozen.a.c);
  // => true
})();

key

alias: keyByVal

Get key string of object by value.

type Key = F.Curry<(obj: Record<string, any>, value: any) => string>;
(() => {
  const obj = { a: 1 };
  const obj1 = { a: 1, b: 1, c: 1 };
  const obj2 = { a: { b: { k: 1 } } };

  fp.key(obj, 1);
  // => a
  fp.key(obj1, 1);
  // => c
  fp.key(obj2, { b: { k: 1 } });
  // => a
  fp.key(obj2, { b: { k: 2 } });
  // => undefined
})();

transformObjectKey

Argument object key transform with case transform function.

type TransformObjectKey = F.Curry<
  (
    transformFn: (orignStr: string) => string,
    obj: Record<string, any>,
  ) => Record<string, any>
>;
(() => {
  const obj = { obj_key: 1 };
  const obj1 = { 'obj-key': 1, obj_key: 2 };
  const nestedObj = {
    objKey: {
      nestedKey: {
        anotherKey: [3],
      },
    },
  };
  const kebabKeyObj = fp.transformObjectKey(fp.kebabCase, obj);
  // => { obj-key: 1 }
  const kebabKeyObj1 = fp.transformObjectKey(fp.kebabCase, nestedObj);
  // => { 'obj-key': { 'nested-key': { 'another-key': [3] } } }
  fp.transformObjectKey(fp.kebabCase, obj1);
  // => obj-key already exist. duplicated property name is not supported.
})();

toCamelcase

alias: toCamelKey

Same with transformObjectKey(lodash.camelCase)

type ToCamelcase = (obj: Record<string, any>) => Record<string, any>;
(() => {
  const obj = { obj_key: 1 };
  const obj1 = { 'obj-key': 1, obj_key: 2 };
  const camelKeyObj = fp.toCamelcase(obj);
  // => { objKey: 1 }
  fp.toCamelcase(obj1);
  // => objKey already exist. duplicated property name is not supported.
})();

toSnakecase

alias: toSnakeKey

Same with transformObjectKey(lodash.snakeCase)

type ToSnakecase = (obj: Record<string, any>) => Record<string, any>;
(() => {
  const obj = { objKey: 1 };
  const obj1 = { objKey: 1, 'obj key': 2 };
  const snakeKeyObj = fp.toSnakecase(obj);
  // => { obj_key: 1}

  fp.toSnakecase(obj1);
  // => obj_key already exist. duplicated property name is not supported.
})();

pascalCase

Argument string transform to pascal case.

type PascalCase = (string) => string;
(() => {
  const pascals = fp.map(fp.pascalCase, [
    '__Foo_Bar__',
    'FOO BAR',
    'fooBar',
    'foo_bar',
    'foo-bar',
  ]);
  // => [FooBar, FooBar, FooBar, FooBar, FooBar]
})();

isDatetimeString

Check argument string can parse with Date.parse function.

type IsDatetimeString = (dateStr: string) => boolean;
(() => {
  const datetimeStrings = [
    'Aug 9, 1995',
    'Wed, 09 Aug 1995 00:00:00 GMT',
    'Wed, 09 Aug 1995 00:00:00',
    '2021/03/14',
    '2021-03-14',
    '2021/03/14 14:21:00',
    '2021-03-14 14:21:00',
    '6 Mar 17 21:22 UT',
    '6 Mar 17 21:22:23 UT',
    '6 Mar 2017 21:22:23 GMT',
    '06 Mar 2017 21:22:23 Z',
    'Mon 06 Mar 2017 21:22:23 z',
    'Mon, 06 Mar 2017 21:22:23 +0000',
  ];

  const invalidDatetimeStrings = ['21:22:23', '20210314'];

  fp.every(fp.pipe(fp.isDatetimeString, fp.equals(true)), datetimeStrings);
  // => true
  fp.every(
    fp.pipe(fp.isDatetimeString, fp.equals(false)),
    invalidDatetimeStrings,
  );
  // => true
})();

ap

Inspired by https://github.com/monet/monet.js/blob/master/docs/MAYBE.md#ap

type Ap = F.Curry<(arg: any, curreid: Function) => any>;
(() => {
  const includesWithAp = fp.pipe(fp.includes, fp.ap('string'));
  const reduceWithAp = fp.pipe(fp.reduce, fp.ap(['f', 'o', 'o']));

  const isIncludeI = includesWithAp('i');
  // => true

  const foo = reduceWithAp((acc, v) => `${acc}${v}`, '');
  // => foo
})();

instanceOf

type InstanceOf = F.Curry<<T>(t: any, arg: T) => boolean>;
(() => {
  class Car {
    constructor(make, model, year) {
      this.make = make;
      this.model = model;
      this.year = year;
    }
  }
  class C {}
  class D {}
  const auto = new Car('Honda', 'Accord', 1998);

  fp.instanceOf(Car, auto);
  // => true
  fp.instanceOf(Object, auto);
  // => true
  fp.instanceOf(C, new C());
  // => true
  fp.instanceOf(C, new D());
  // => false

  fp.instanceOf(String, 'string');
  // => false
  fp.instanceOf(String, new String('string'));
  // => true
  fp.instanceOf(Object, {});
  // => true
})();

removeByIndex

alias: removeByIdx

type RemoveByIndex = F.Curry<
  <R>(index: number | string, targetArray: R[]) => R[]
>;
(() => {
  const arr = [1, 2, 3];
  const secondRemoved = fp.removeByIndex(1, arr);

  // argument array should not be mutated.
  arr;
  // => [1, 2, 3]
  secondRemoved;
  // => [1, 3]
})();

removeLast

type RemoveLast = (target: string | any[]) => string | any[];
(() => {
  const arr = [1, 2, 3];
  const lastRemoved = fp.removeLast(arr);

  // argument array should not be mutated.
  arr;
  // => [1, 2, 3]
  lastRemoved;
  // => [1, 2]
})();

append

alias: concat

type Append = F.Curry<<T>(arr: T[], arg: T | T[]) => T[]>;
(() => {
  const arr = [1];
  const appended = fp.append(arr, 34);

  // argument array should not be mutated.
  arr;
  // => [1]
  appended;
  // => [1, 34]
  fp.append(arr, [2, 3, 4]);
  // => [1, 2, 3, 4]
})();

prepend

type Prepend = F.Curry<<T>(arr: T[], arg: T | T[]) => T[]>;
(() => {
  const arr = [1];
  const prepended = fp.prepend(arr, 34);

  // argument array should not be mutated.
  arr;
  // => [1]
  prepended;
  // => [34, 1]
  fp.prepend(arr, [2, 3, 4]);
  // => [2, 3, 4, 1]
})();

mapWithKey

alias: mapWithIdx, mapWithIndex

Same with \fp.map.convert({ cap: false})

type MapWithKey = F.Curry<
  <T, K extends keyof T, R>(
    iteratee: (value: T[K], key: K) => R,
    collection: T,
  ) => R[]
>;
(() => {
  const arr = [3, 4, 5];
  const getIdxs = fp.mapWithKey((v, i) => i);

  getIdxs(arr);
  // => [0, 1, 2]
  getIdxs({ a: 1, b: 2 });
  // => ['a', 'b']
})();

forEachWithKey

result will same with input (effect function)

alias: forEachWithIdx, forEachWithIndex

Same with \fp.map.forEach({ cap: false})

type TforEachWithKey = F.Curry<
  <T, K extends keyof T>(
    iteratee: (value: T[K], key: K) => T,
    collection: T,
  ) => T
>;
(() => {
  const arr = [3, 4, 5];
  const getIdxs = fp.forEachWithKey((v, i) => i);

  getIdxs(arr);
  // => [3, 4, 5]
  getIdxs({ a: 1, b: 2 });
  // => { a: 1, b: 2 }
})();

reduceWithKey

alias: reduceWithIdx, reduceWithIndex

Same with \fp.reduce.convert({ cap: false })

type ReduceWithKey = F.Curry<
  <T, K extends keyof T, R>(
    iteratee: (acc: R, value: T[K], key: K) => R,
    acc: R,
    collection: T,
  ) => R
>;
(() => {
  const arr = [3, 4, 5];
  const getIdxs = fp.reduceWithKey((acc, v, i) => fp.concat(acc, i), []);

  getIdxs(arr);
  // => [0, 1, 2]
  getIdxs({ a: 1, b: 2 });
  // => ['a', 'b']
})();

isFalsy

type isFalsy = (arg: any) => boolean;
() => {
  const falsies = [undefined, null, 0, -0, NaN, false, ''];
  const notFalsies = [[], '0', 'false', {}, () => {}];
  const composer = fp.pipe(fp.map(fp.isFalsy), fp.every(fp.equals(true)));

  composer(falses);
  // => true
  composer(notFalsies);
  // => false
};

isTruthy

type IsTruthy = (arg: any) => boolean;
(() => {
  const falsies = [undefined, null, 0, -0, NaN, false, ''];
  const notFalsies = [[], '0', 'false', {}, () => {}];

  const composer = fp.pipe(fp.map(fp.isTruthy), fp.every(fp.equals(false)));

  composer(falses);
  // => true
  composer(notFalsies);
  // => false
})();

delayAsync

alias: sleep

type DelayAsync = (ms: number) => Promise<void>;
(async () => {
  await fp.delayAsync(300); // 300ms delay
})();