Skip to content

Latest commit

History

History
933 lines (673 loc) 路 25.1 KB

README.md

File metadata and controls

933 lines (673 loc) 路 25.1 KB

馃П cool/fp

cool/fp is a library designed for functional programming enthusiasts. It covers a wide range of utility functions, making it a one-stop solution for many functional programming needs.

If you're familiar with libraries like Lodash or Ramda, think of cool/fp as an alternative.

馃挮 Key features

  • Immutable Operations: All methods in cool/fp return a new instance, ensuring the original data remains unchanged. (see: no mutation)

  • No New Data Types: Unlike some alternatives, cool/fp doesn't introduce new data types or structures.

  • Pure Functions: Embrace the predictability and reliability provided by the functional programming approach, as functions always produce the same output for the same input. (see: determinism)

  • No Dependencies: cool/fp is a standalone library with no external dependencies.

  • Type Support: Written in TypeScript,cool/fp` provides full-fledged support for TypeScript users.

馃摎 Key functions

... and more!

馃殌 Getting Started with Functional Programming (FP)

Functional programming is a programming paradigm (a way of writing programs) that emphasizes using functions as first-class citizens for designing our code. It is a declarative programming approach in which functions take inputs and return outputs, rather than a sequence of imperative statements.

By constructing your code with pure functions, you ensure that the same input always produces the same output. This results in predictable, reliable and testable code with no side effects during runtime.

Learn more about Functional Programming.

馃洜 Usage and API Reference

Here you'll find a list of utility functions provided by cool/fp along with brief descriptions and usage examples. Dive in and explore the usage of functional programming paradigms.

appendToArray(source, ...items)

adds new item(s) to the end of an array or generator without mutating the original.

import { appendToArray } from "$cool/fp/append-to-array.ts";

const source = ["a", "b"];
const newOne = appendToArray(source, "c");

// output: Result: ['a','b','c']
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

appendToObject(source, ...items)

adds new item(s) to an object without changing the original.

import { appendToObject } from "$cool/fp/append-to-object.ts";

const source = { a: 1, b: 2 };
const newOne = appendToObject(source, { c: 3 });

// output: Result: {'a':1,'b':2,'c':3}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

associateArray(source, selectorFn)

transforms an array or generator into an object with keys are determined by a selector function (selectorFn).

import { associateArray } from "$cool/fp/associate-array.ts";

// associate array - basic sample
const categories = [
  { id: 1, name: "foo" },
  { id: 2, name: "bar" },
  { id: 3, name: "baz" },
];

const result = associateArray(categories, (category) => category.id);

/* outputs:
  {
    "1": { id: 1, name: "foo" },
    "2": { id: 2, name: "bar" },
    "3": { id: 3, name: "baz" }
  }
*/
console.dir(result);

associateObject(source, selectorFn)

transforms an object into another object with keys are determined by a selector function (selectorFn).

import { associateObject } from "$cool/fp/associate-object.ts";

// associate object - basic sample
const categories = {
  a: { id: 1, name: "foo" },
  b: { id: 2, name: "bar" },
  c: { id: 3, name: "baz" },
};

const result = associateObject(categories, (category) => category.id);

// outputs:
// {
//   "1": { id: 1, name: "foo" },
//   "2": { id: 2, name: "bar" },
//   "3": { id: 3, name: "baz" }
// }
console.dir(result);

compose(...functionsForComposition)

composes multiple functions into a single function by passing the results of one function as input to the next. unlike pipe, it executes the functions from reverse order (right to left).

import { compose } from "$cool/fp/compose.ts";

// compose - slug sample
const lower = (x) => x.toLowerCase();
const chars = (x) => x.replace(/[^\w \-]+/g, "");
const spaces = (x) => x.split(" ");
const dashes = (x) => x.join("-");

const slug = compose(dashes, spaces, chars, lower);

const message = slug("Hello World!");

// outputs 'slug: hello-world'
console.log(`slug: ${message}`);

curry(targetFunction, ...argumentsToBePrepended)

transforms a function into a curried version, allowing for partial application of arguments from the left.

import { curry } from "$cool/fp/curry.ts";

// curry - sum sample
const sum = (a, b) => a + b;

const sumWith5 = curry(sum, 5);

const result = sumWith5(3);

// outputs 'result: 8'
console.log(`result: ${result}`);

curryRight(targetFunction, ...argumentsToBeAppended)

transforms a function into a curried version, allowing for partial application of arguments from the right.

import { curryRight } from "$cool/fp/curry-right.ts";

// curryRight - sum sample
const dec = (a, b) => a - b;

const decWith5 = curry(dec, 5);

const result = decWith5(3);

// outputs 'result: -2'
console.log(`result: ${result}`);

decorate(functionToDecorate, decoratorFn)

enhances or alters a function's behavior using a decorator function (decoratorFn).

import { decorate } from "$cool/fp/decorate.ts";

// decorate - calculator sample
let generator = () => 5;
generator = decorate(generator, (func) => func() * 2);
generator = decorate(generator, (func) => func() + 1);

// outputs: 'generated: 11'
console.log(`generated: ${generator()}`);

deepCopy(source)

creates a deep copy of the given data structure while preserving its constructor.

import { deepCopy } from "$cool/fp/deep-copy.ts";

class dummy {}

const source = new dummy();
const newOne = deepCopy(source);

// output: Result: class dummy {}
console.log("Result:", newOne.constructor);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

deepMerge(source, other)

merges two data structures deeply to produce a new structure with combined items.

import { deepMerge } from "$cool/fp/deep-merge.ts";

const source = {
  a: {
    b: [1, 2, 3],
    c: {
      d: 4,
    },
  },
};

const other = {
  a: {
    b: [55],
  },
  e: "hello",
};

const newOne = deepMerge(source, other);

// output: Result: {
//   a: {
//     b: [55],
//     c: {
//       d: 4,
//     },
//   },
//   e: "hello",
// }
console.log("Result:", newOne);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

dispatcher(initialState, mutatorFns) (awaitable)

manages state mutations in a controlled manner using an initial state and a set of functions (mutatorFns).

import { dispatcher } from "$cool/fp/dispatcher.ts";

// dispatcher - state mutation sample
const initialState = { quarter: 1, year: 2018, sum: 1 };

const actionAdd5 = (state, next) => next({ ...state, sum: state.sum + 5 });
const actionDiv2 = (state, next) => next({ ...state, sum: state.sum / 2 });

// outputs 'new state is: {"quarter":1,"year":2018,"sum":3}'
dispatcher(initialState, [actionAdd5, actionDiv2])
  .then((state) => console.log(`new state is: ${JSON.stringify(state)}`));

dispatcher(initialState, mutatorFns, subscribers) (awaitable)

manages state mutations in a controlled manner using an initial state and a set of functions (mutatorFns). additionally, it notifies subscribers about the state changes.

import { dispatcher } from "$cool/fp/dispatcher.ts";

// dispatcher - action logger sample
const initialState = { quarter: 1, year: 2018, sum: 1 };

const actionAdd5 = (state, next) => next({ ...state, sum: state.sum + 5 });
const actionDiv2 = (state, next) => next({ ...state, sum: state.sum / 2 });

const logger = (x) => console.log("INFO", x);

/* outputs:
   INFO { action: 'actionAdd5',
     previousState: { quarter: 1, year: 2018, sum: 1 },
     newState: { quarter: 1, year: 2018, sum: 6 } }
   INFO { action: 'actionDiv2',
     previousState: { quarter: 1, year: 2018, sum: 6 },
     newState: { quarter: 1, year: 2018, sum: 3 } }
   new state is: {"quarter":1,"year":2018,"sum":3}'
*/
dispatcher(initialState, [actionAdd5, actionDiv2], [logger])
  .then((state) => console.log(`new state is: ${JSON.stringify(state)}`));

distinctArray(source, selectorFn)

filters out duplicate values in an array or generator based on a selector function (selectorFn).

// TODO

distinctObject(source, selectorFn)

filters out duplicate values in an object based on a selector function (selectorFn).

// TODO

dropFromArray(source, n)

skips the first n item(s) in an array or generator.

import { dropFromArray } from "$cool/fp/drop-from-array.ts";

const source = ["a", "b", "c"];
const newOne = dropFromArray(source, 1);

// output: Result: ['b','c']
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

dropFromObject(source, n)

Skips the first n item(s) in an object.

import { dropFromObject } from "$cool/fp/drop-from-object.ts";

const source = { a: 1, b: 2, c: 3 };
const newOne = dropFromObject(source, 1);

// output: Result: {'b':2,'c':3}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

emitter(events, eventName, eventParameters) (awaitable)

emits events to subscribed listeners.

import { emitter } from "$cool/fp/emitter.ts";

// emitter - static pub/sub sample
const subscriberOne = (value) =>
  console.log(`subscriberOne had value ${value}`);
const subscriberTwo = (value) =>
  console.log(`subscriberTwo had value ${value}`);

const events = {
  printToConsole: [subscriberOne, subscriberTwo],
};

/* outputs:
   subscriberOne had value 5
   subscriberTwo had value 5
*/
emitter(events, "printToConsole", [5]);

emitter(events, eventName, eventParameters, subscribers) (awaitable)

emits events to subscribed listeners. additionally, it notifies subscribers about the event.

import { emitter } from "$cool/fp/emitter.ts";

// emitter - event logger sample
const subscriberOne = (value) =>
  console.log(`subscriberOne had value ${value}`);
const subscriberTwo = (value) =>
  console.log(`subscriberTwo had value ${value}`);

const logger = (x) => console.log("INFO", x);

const events = {
  printToConsole: [subscriberOne, subscriberTwo],
};

/* outputs:
   INFO { event: 'printToConsole',
     subscriber: 'subscriberOne',
     args: [ 5 ] }
   subscriberOne had value 5
   INFO { event: 'printToConsole',
     subscriber: 'subscriberTwo',
     args: [ 5 ] }
   subscriberTwo had value 5
*/
emitter(events, "printToConsole", [5], [logger]);

filterArray(instance, predicateFn)

returns a new array containing only the items that satisfy the provided predicate function (predicateFn).

import { filterArray } from "$cool/fp/filter-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = filterArray(source, (x) => x <= 3);

// output: Result: [1,2,3]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

filterObject(instance, predicateFn)

returns a new object containing only the items that satisfy the provided predicate function (predicateFn).

import { filterObject } from "$cool/fp/filter-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = filterObject(source, (x) => x <= 3);

// output: Result: {'a':1,'b':2,'c':3}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

iterate(iterable, fn) (awaitable)

iterates over an iterable (like a generator) and applies a function (fn) to each item.

import { iterate } from "$cool/fp/iterate.ts";
import { compose } from "$cool/fp/compose.ts";

// iterate - url fetcher example
const generator = function* () {
  yield "http://localhost/samples/1"; // { value: 1 }
  yield "http://localhost/samples/2"; // { value: 2 }
  yield "http://localhost/samples/3"; // { value: 3 }
};

const fetchUrl = async (url) => {
  const response = await fetch(url);
  const document = await response.json();

  return document.value;
};

const add5 = async (value) => await value + 5;
const printToConsole = async (value) => {
  console.log(await value);
};

/* outputs:
   value is 6
   value is 7
   value is 8
*/
iterate(
  generator(),
  compose(fetchUrl, add5, printToConsole),
);

mapArray(instance, predicate)

transforms each item in an array or generator using a provided predicate function (predicateFn).

import { mapArray } from "$cool/fp/map-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = mapArray(source, (x) => x - 1);

// output: Result: [0,1,2,3,4]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

mapObject(instance, predicate)

transforms each property in an object using a provided predicate function (predicateFn).

import { mapObject } from "$cool/fp/map-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = mapObject(source, (value, key) => ({ [key]: value - 1 }));

// output: Result: {'a':0,'b':1,'c':2,'d':3,'e':4}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

mergeArrays(...sources)

combines two or more arrays into a single array.

import { mergeArrays } from "$cool/fp/merge-arrays.ts";

const source1 = [1, 2, 3];
const source2 = [4, 5];
const newOne = mergeArrays(source1, source2);

// output: Result: [1,2,3,4,5]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

mergeObjects(...sources)

combines two or more objects into a single object.

import { mergeObjects } from "$cool/fp/merge-objects.ts";

const source1 = { a: 1, b: 2, c: 3 };
const source2 = { d: 4, e: 5 };
const newOne = mergeObjects(source1, source2);

// output: Result: {'a':1,'b':2,'c':3,'d':4,'e':5}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

mutate(source, mutatorFn)

creates a copy of a data structure and applies a mutator function (mutatorFn) to it. copies an instance with its constructor, with specific mutation.

import { mutate } from "$cool/fp/mutate.ts";

class dummy {
  constructor() {
    this.items = [];
  }
}

const source = new dummy();
const newOne = mutate(source, (x) => x.items.push(6));

// output: Result: class dummy {}
console.log("Result:", newOne.constructor);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

pickFromArray(source, items)

Returns an object containing items that match and don't match the provided criteria from an array or generator.

import { pickFromArray } from "$cool/fp/pick-from-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = pickFromArray(source, [2, 3, 6]);

// output: Result: {'items':[2,3],'rest':[1,4,5]}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

pickFromObject(source, keys)

returns an object containing items that match and don't match the provided keys from an object.

import { pickFromObject } from "$cool/fp/pick-from-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = pickFromObject(source, ["b", "c", "f"]);

// output: Result: {'items':{'b':2,'c':3},'rest':{'a':1,'d':4,'e':5}}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

pipe(...functionsForComposition)

composes multiple functions into a single function. it is done by passing the results of a function as an input to another one. unlike compose, it executes the functions from straight order (left to right).

import { pipe } from "$cool/fp/pipe.ts";

// pipe - slug sample
const lower = (x) => x.toLowerCase();
const chars = (x) => x.replace(/[^\w \-]+/g, "");
const spaces = (x) => x.split(" ");
const dashes = (x) => x.join("-");

const slug = pipe(lower, chars, spaces, dashes);

const message = slug("Hello World!");

// outputs 'slug: hello-world'
console.log(`slug: ${message}`);

prependToArray(source, ...items)

adds new item(s) to the beginning of an array or generator without mutating the original.

import { prependToArray } from "$cool/fp/prepend-to-array.ts";

const source = ["b", "c"];
const newOne = prependToArray(source, "a");

// output: Result: ['a','b','c']
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

prependToObject(source, ...items)

adds new item(s) to the beginning of an object without changing the original.

import { prependToObject } from "$cool/fp/prepend-to-object.ts";

const source = { b: 2, c: 3 };
const newOne = prependToObject(source, { a: 1 });

// output: Result: {'a':1,'b':2,'c':3}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeFirstMatchFromArray(source, predicateFn)

removes the first item in an array or generator that matches the provided predicate function (predicateFn).

import { removeFirstMatchFromArray } from "$cool/fp/remove-first-match-from-array.ts";

const source = [1, 5, 2, 3, 4, 5];
const newOne = removeFirstMatchFromArray(source, (x) => x === 5);

// output: Result: [1,2,3,4,5]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeFirstMatchFromObject(source, predicateFn)

removes the first property in an object that matches the provided predicate function (predicateFn).

import { removeFirstMatchFromObject } from "$cool/fp/remove-first-match-from-object.ts";

const source = { a: 1, f: 5, b: 2, c: 3, d: 4, e: 5 };
const newOne = removeFirstMatchFromObject(source, (x) => x === 5);

// output: Result: {'a':1,'b':2,'c':3,'d':4,'e':5}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeIndexFromArray(source, ...items)

removes items from an array or generator based on their indices.

import { removeIndexFromArray } from "$cool/fp/remove-index-from-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = removeIndexFromArray(source, 2, 3);

// output: Result: [1,2,5]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeValueFromArray(source, ...items)

removes specified values from an array or generator.

import { removeValueFromArray } from "$cool/fp/remove-value-from-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = removeValueFromArray(source, 2, 3);

// output: Result: [1,4,5]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeKeyFromObject(source, ...keys)

removes items from an object based on their keys.

import { removeKeyFromObject } from "$cool/fp/remove-key-from-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = removeKeyFromObject(source, "b", "c");

// output: Result: {'a':1,'d':4,'e':5}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

removeValueFromObject(source, ...values)

Removes items from an object based on their values.

import { removeValueFromObject } from "$cool/fp/remove-value-from-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = removeValueFromObject(source, 2, 3);

// output: Result: {'a':1,'d':4,'e':5}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

reverseArray(source)

reverses the order of items in an array or generator.

import { reverseArray } from "$cool/fp/reverse-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = reverseArray(source);

// output: Result: [5,4,3,2,1]
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

reverseObject(source)

reverses the order of items in an object.

import { reverseObject } from "$cool/fp/reverse-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = reverseObject(source);

// output: Result: {'e':5,'d':4,'c':3,'b':2,'a':1}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

splitArray(source, index)

divides an array or generator into two parts based on the provided index.

import { splitArray } from "$cool/fp/split-array.ts";

const source = [1, 2, 3, 4, 5];
const newOne = splitArray(source, 3);

// output: Result: {'items':[1,2,3],'rest':[4,5]}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

splitObject(source, index)

divides an object into two parts based on the provided index.

import { splitObject } from "$cool/fp/split-object.ts";

const source = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const newOne = splitObject(source, 3);

// output: Result: {'items':{'a':1,'b':2,'c':3},'rest':{'d':4,'e':5}}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

takeFromArray(source, n)

returns the first n items from an array or generator.

import { takeFromArray } from "$cool/fp/take-from-array.ts";

const source = ["a", "b", "c"];
const newOne = takeFromArray(source, 2);

// output: Result: ['a','b']
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

takeFromObject(source, number)

returns the first n items from an object.

import { takeFromObject } from "$cool/fp/take-from-object.ts";

const source = { a: 1, b: 2, c: 3 };
const newOne = takeFromObject(source, 2);

// output: Result: {'a':1,'b':2}
console.log(`Result: ${JSON.stringify(newOne)}`);
// output: Is Same: false
console.log(`Is Same: ${source === newOne}`);

馃敆 For further details such as requirements, licensing and support guide, please visit the main cool repository.