0.3.0 • Published 2 years ago

@warangel580/utils v0.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

Utils

Common operations on the most used data structures : single values (strings, numbers, ...), arrays and objects.

Dealing with only "simple" types allows to model complex systems with only simple functions usable on everything.

As a design philosophy, all functions use "data" as a first argument and use no side-effects so they're easily reusable on iterable data.

Installation

npm install @warangel580/utils

or

yarn add @warangel580/utils

Import

// ES6
import { transform, get, set } from "@warangel580/utils";
// NodeJS
const { transform, get, set } = require("@warangel580/utils");

API

Note: _ means "any other parameter"

Types

isNil(data)

Return true if data is null or undefined

isNil(undefined) // => true
isNil(null)      // => true
isNil(_)         // => false

isFunction(data)

Return true if data is a function

isFunction(() => {}) // => true
isFunction(_)        // => false

isArray(data)

Return true if data is an array

isArray([/* ... */]) // => true
isArray(_)           // => false

isObject(data)

Return true if data is an array

isObject({/* ... */}) // => true
isObject(_)           // => false

isIterable(data)

Return true if data is iterable, i.e. is an array or an object

isIterable([/* ... */]) // => true
isIterable({/* ... */}) // => true
isIterable(_)           // => false

Iterators

reduce(data, fn, initialValue)

Like Array.reduce but works on objects too.

Iterates on data by doing next = fn(current, value, key, data), starting with initialValue as current.

If data is not iterable, it returns initialValue.

const add = (a, b) => a + b;

reduce([1, 2, 3],          add, 0) // => 6
reduce({a: 1, b: 2, c: 3}, add, 0) // => 6
reduce(undefined,          add, 0) // => 0

transform(initialValue, data, fn)

Like reduce but arguments are swapped for readability, for example when we're trying to "transform" an existing value instead of creating a new one.

transform({a: 1}, [{b: 2}, {c: 3}], (current, next) => {
  return Object.assign(current, next);
}) // => {a: 1, b: 2, c: 3}

map(data, fn)

Like Array.map but works on objects too.

Iterates on data by doing value = fn(value, key, data)

If data is not iterable, it returns data.

const inc = x => x + 1;

map([1, 2, 3],          inc) // => [2, 3, 4]
map({a: 1, b: 2, c: 3}, inc) // => {a: 2, b: 3, c: 4}
map(null,               inc) // => null

filter(data, fn)

Like Array.filter but works on objects too.

Iterates on data by doing keep = fn(value, key, data)

If data is not iterable, it returns data.

const isEven = x => x % 2 == 0;

reduce([1, 2, 3],          isEven) // => [2]
reduce({a: 1, b: 2, c: 3}, isEven) // => {b: 2}
reduce(null,               isEven) // => null

each(data, fn)

Like Array.forEach but works on objects too.

Iterates on data by doing fn(value, key, data)

If data is not iterable, it returns data.

const log = v => console.log(v)

each([1, 2, 3],          log) // logs(1) then (2), (3)
each({a: 1, b: 2, c: 3}, log) // logs(1) then (2), (3)
each(null,               log) // (nothing happens)

Side-effects

debug(data, ...rest)

Returns data after console.log(...rest, data) so it's easy to add debug() to existing code

// => console.log("a", a, "b", b, "=>", a + b)
// => return a + b
return debug(a + b, "a", a, "b", b, "=>");

tap(data)

Returns data after applying side-effect to allow chaining

// Adding a user in a single line
return tap(users, users => users.push(user));

copy(data)

Returns a shallow-copy of data to ensure that it doesn't change later with side-effects.

let prev = [1, 2, 3, 4];
let next = copy(prev);

// Editing number with side-effects
next.push(5);

// Previous hasn't changed
prev // => [1, 2, 3, 4]

clone(data)

Same as copy, but returns a deep copy (slow!) of data.

parseJson(data, defaultValue = {})

Parse json without failing.

parseJson('{"foo":"bar"}')  // => {foo: "bar"}
parseJson('{invalid json}') // => {}
parseJson('{invalid json}', undefined) // => undefined

toJson(data, pretty = false, replacer = null)

Transforms data into JSON representation.

pretty can be a number, which is the number of spaces used for indentation (2 by default if pretty === true)

toJson({foo: "bar"})  // => '{"foo":"bar"}'
toJson({a: 1, b: 2}, true)  /* => '{
  "a": 1,
  "b": 2
}' */

tryCatch(fn, onCatch)

Returns fn result or run/return onCatch if something goes wrong.

let fail = () => { throw 'ERR' }

tryCatch(fail)             // => undefined
tryCatch(fail, 42)         // => 42
tryCatch(fail, err => err) // => 'ERR'

Getters - Setters

get(data, path, notFoundValue = undefined)

Get value from data.

let userId   = get(response, ['user', userId, 'comments'], [])
// => <comments> or []
let userName = get(user, 'active', false);
// => <active> or false

set(data, path, newValue)

Set a value (without side-effects) in a deep data tree of values.

let user = {
  username: "warangel580",
  game: {
    score: 10,
  }
};

user = set(user, 'active', true);
user = set(user, ['game', 'state'], 'done');
user = set(user, ['game', 'score'], s => s * 2);

user /* => {
  username: "warangel580",
  active: true,
  game: {
    state: 'done',
    score: 20,
  }
} */

setUnsafe(data, path, newValue)

Like set but doesn't copy data before editing it, which is way faster on large datasets.

You have the responsability to copy data if you need to avoid side-effects.

let before = {};

after = setUnsafe(before, 'foo', 'bar');

before /* => {
  foo: "bar",
} */

after /* => {
  foo: "bar",
} */

Data

size(data)

Get data size, like Array.length.

size([1, 2, 3])                     // => 3
size({a: 1, b, 2, c: {x: 3, y: 4}}) // => 3
size(null)                          // => undefined

keys(data)

Get data keys, like Object.keys().

keys({a: 1, b, 2, c: {x: 3, y: 4}}) // ['a', 'b', 'c']

values(data)

Get data values, like Object.values().

values({a: 1, b, 2, c: {x: 3, y: 4}}) // => [1, 2, {x: 3, y: 4}]

entries(data)

Get data entries, like Object.entries().

let data = {a: 1, b: {x: 2, y: [3, 4]}, c: ['foo', 'bar']};

entries(data) // => [['a', 1], ['b', {x: 2, y: [3,4]}], ['c', ['foo', 'bar']]]

randomIn(data)

Get a random value in data

// Results may vary ;)
randomIn([1, 2, 3])          // => 3
randomIn([1, 2, 3])          // => 2
randomIn({a: 1, b: 2, c: 3}) // => 1
randomIn({a: 1, b: 2, c: 3}) // => 3

randomEntryIn(data)

Get a random [key, value] in data

// Results may vary ;)
randomEntryIn([1, 2, 3])          // => [2, 3]
randomEntryIn([1, 2, 3])          // => [1, 2]
randomEntryIn({a: 1, b: 2, c: 3}) // => ['a', 1]
randomEntryIn({a: 1, b: 2, c: 3}) // => ['c', 3]

randomKeyIn(data)

Get a random value in data

// Results may vary ;)
randomIn([1, 2, 3])          // => 2
randomIn([1, 2, 3])          // => 0
randomIn({a: 1, b: 2, c: 3}) // => 'a'
randomIn({a: 1, b: 2, c: 3}) // => 'c'

sort(data, comparator)

Sort data (without side-effects), like Array.sort().

Note that object order is "temporary" (not enforced by javascript) but still useful for displaying data.

sort({a:1, b:7, c:4}, (v1, v2) => v2 - v1) // => {b:7, c:4, a:1}

sortUnsafe(data, comparator)

Like sortUnsafe but doesn't copy data before sorting it, which is way faster on large datasets.

You have the responsability to copy data if you need to avoid side-effects.

sortUnsafe({a:1, b:7, c:4}, (v1, v2) => v2 - v1) // => {b:7, c:4, a:1}

Arrays

pushLast(data, ...values)

Pure version of Array.push

pushLast(["a"], "b", "c") // => ["a", "b", "c"]

pushLastUnsafe(data, ...values)

Like pushLast but doesn't copy data before editing it, which is way faster on large datasets.

You have the responsability to copy data if you need to avoid side-effects.

pushLastUnsafe(["a"], "b", "c") // => ["a", "b", "c"]

pushFirst(data, ...values)

Pure version of Array.unshift

pushFirst(["a"], "b", "c") // => ["b", "c", "a"]

pushFirstUnsafe(data, ...values)

Like pushFirst but doesn't copy data before editing it, which is way faster on large datasets.

You have the responsability to copy data if you need to avoid side-effects.

pushFirstUnsafe(["a"], "b", "c") // => ["b", "c", "a"]

popFirst(data)

Pure version of Array.pop, returns [firstElement, ...otherElements]

popFirst(["a", "b", "c"]) // => ["a", ["b", "c"]]

popLast(data)

Pure version of Array.shift, returns [...elements, lastElement]

popLast(["a", "b", "c"]) // => [["a", "b"], "c"]

concat(...datas)

Return all arrays concatenated

concat(["a", "b"], ["c", "d"], ["e"]) // => ["a", "b", "c", "d", "e"]

partition(data, n, ...datas)

Concats arrays then cut them in n-sized arrays

concat(["1", "2"], 3, ["3", "4"], ["5"], ["6"]) // => [["1", "2", "3"], ["4", "5", "6"]]

toPairs(...datas)

Returns a partition of size 2, useful for making pairs.

toPairs(["1", "2"], ["3", "4"], ["5"], ["6"]) // => [["1", "2"], ["3", "4"], ["5", "6"]]

Objects

merge(...datas, fn?)

Merge objects with Object.assign or fn(current, next) if given

merge({a:1}, {b:2}, {b:3, c:4}) // => {a:1, b:3, c:4}
merge({b:1}, {b:2}, {b:3}, (current, next) => {
  return transform(current, next, (v, k) => {
    return get(current, k, 0) + v;
  });
}) // {b: 6}

Values

or(...datas)

Returns the "most truthy" value given, useful for default values

or(undefined, [])          // => []
or({a: 1},    {})          // => {}
or(undefined, null, "foo") // => "foo"

range(size)

Returns an array of size filled with indexes

range(5) // => [0, 1, 2, 3, 4]

when(...kvs)

TODO: not sure of API for this one, going to improve it later

match(data, kvs)

TODO: not sure of API for this one, going to improve it later

Functions

using(...values, fn)

Use multiple unnamed expressions in a function, which allows local naming without let.

using(1 + 1, 3 * 4, (v1, v2) => v1 * v2) // => 24

call(data, fnName, ...args)

Returns data[fnName](...args)

call(["a", "b", "c"], "slice", -2) // => ["b", "c"]

defer(fn, ...args)

Simplify callbacks that use data as first arg

map(array, value => get(value, 'key'))
// becomes
map(array, defer(get, 'key'))

If you alias defer to _, you can write

map(array, _(get, 'key'))

pipe(data, ...fns)

Apply fns to data successively

// get admin user names
pipe(users,
  _(filter, _(get, 'isAdmin', false)),
  _(map,    _(get, 'name'))
)

Updating package

0.1.0

2 years ago

0.3.0

2 years ago

0.2.0

2 years ago

0.1.1

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago