@almela/obx
v0.0.6
Published
Fast & Lightweight Object Manipulation Library for Javascript
Downloads
28
Readme
About
obx is a super fast, tiny, well documented, and battle-tested object manipulation library for Javascript.
Getting Started
Installation
Install with npm
npm i @almela/obx
or with yarn
yarn add @almela/obx
Usage
In your file, simply add
import * as obx from '@almela/obx'
or simply import select functions
import { eq, cp } from '@almela/obx'
Docs
For even more examples, see the tests.
Functions
- eq(a, b, params)
- cp(o, params)
- get(o, p)
- set(o, p, v)
- len(o, params)
- map(o, fn, params)
- reduce(o, fn, a, params)
- zip(objs, params)
- sub(o, s, params)
- add(o, a, params)
- isEmptyObj(o)
- isEmptyArr(a)
eq
Assert that two objects are equal. Objects are equal if they have the same keys and values.
Params
a
: Object - Object 1b
: Object - Object 2params
: Object - Parameters object[.depth]
: number - Depth of equality check. Defaults to infinity
Object order doesn't matter
obx.eq(
{ foo: "bar", bar: "baz" },
{ bar: "baz", foo: "bar" }
)
// -> true
With Arrays
obx.eq([1, 2, 3], [1, 2, 3])
// -> true
Array order does matter!
obx.eq([1, 2, 3], [3, 2, 1])
// -> false
Custom depth
obx.eq({ foo: "bar" }, { foo: "baz" }, { depth: 0 })
// -> true
obx.eq({ foo: { bar: "baz" } }, { foo: {} }, { depth: 1 })
// -> true
obx.eq({ foo: { bar: "baz" } }, { foo: {} }, { depth: 2 })
// -> false
Functions
// Unfortunately, functions are basically impossible to
// diff. `eq` assumes that all functions are the same.
obx.eq({ foo: (x) => x + 1 }, { foo: (x) => x + 2 })
// -> true
cp
Deep copy an object.
Params
o
: Object - Object to copyparams
: Object - Parameters object[.depth]
: number - Depth of copy. Defaults to infinity
Copy by value, not by reference
const a = {
foo: {
bar: 'baz'
}
}
const b = obx.cp(a)
a.foo.bar = 'bar'
console.log(b)
// object remains the same
// -> {
// foo: {
// bar: 'baz'
// }
// }
get
Get value from object.
Params
o
: Object - The objectp
: String - Value path
Get a deep key
obx.get("foo.bar", {
foo: {
bar: "baz"
}
});
// -> "baz"
Also works with arrays
obx.get("foo.2.baz", {
foo: [
{
foo: 'foo'
},
{
bar: 'bar'
},
{
baz: 'baz'
}
]
});
// -> "baz"
No key? No problem.
obx.get("foo.2.baz", {
foo: 'bar'
})
// -> null
set
Set value in object.
Params
o
: Object - The object to be mutatedp
: String - Value pathv
: Object - Value to set
const o = {}
obx.set(o, "foo.bar.baz.haz", "hello")
// -> {
// foo: {
// bar: {
// baz: {
// haz: "hello"
// }
// }
// }
// }
const o = {}
obx.set(o, "foo.2.foo", 'bar')
// o -> {
// foo: [<2 empty slots>, {
// foo: 'bar'
// }]
// }
len
Recursively find the number of keys of an object. Note that this includes object-valued keys.
Params
o
: Object - Object to find length ofparams
: Object - Parameters object[.depth]
: number - Depth of len check. Defaults to infinity
Simple object
obx.len({ foo: 'bar', bar: 'baz' }) // -> 2
Recursive object, depth 1
// Here depth is only computed at the top level
obx.len({ foo: 'bar', bar: { bar: 'baz', baz: [1, 2, 3] } }, { depth: 1 }) // -> 2
Recursive object, infinite depth
// Note: array keys are counted
obx.len({ foo: 'bar', bar: { bar: 'baz', baz: [1, 2, 3] } }) // -> 7
map
Recursively map though all entries of an object. Note that map will also iterate through object-valued keys.
Params
o
: Object - Object to map throughfn
: function - Callback function. Contains [k, v] pair, path, objectparams
: Object - Parameters object[.depth]
: number - Depth of map. Defaults to infinity
Basic Mapping
const o = {
foo: "bar",
bar: "baz",
baz: "foo",
};
// Note that map will callback on every value of the object, including sub objects!
const emphasis = ([_, v]) => (v instanceof Object ? v : v + "!");
// Note that depth could be anything here, since this is just a flat object.
obx.map(o, emphasis, { depth: 1 });
// -> {
// foo: "bar!",
// bar: "baz!",
// baz: "foo!",
// }
Recursive Mapping, low depth
const o = {
foo: "bar",
bar: {
baz: "foo",
},
};
// Note that map will callback on every value of the object, including sub objects!
const emphasis = ([_, v]) => (v instanceof Object ? v : v + "!");
obx.map(o, emphasis, { depth: 1 });
// -> {
// foo: "bar!",
// bar: {
// baz: "foo",
// }
// }
//
// Note that the inner key is unchanged.
Recursive Mapping, high depth
const o = {
foo: "bar",
bar: [
{ foo: "bar", bar: "baz" },
{ foo: "bar", bar: "baz" },
{ foo: "bar", bar: "baz" },
],
raz: "faz",
};
// Note that map will callback on every value of the object, including sub objects!
const emphasis = ([_, v]) => (v instanceof Object ? v : v + "!");
obx.map(o, emphasis);
// -> {
// foo: "bar!",
// bar: [
// { foo: "bar!", bar: "baz!" },
// { foo: "bar!", bar: "baz!" },
// { foo: "bar!", bar: "baz!" },
// ],
// raz: "faz!",
// }
reduce
Recursively reduce through all entries of an object. Note that reduce will also iterate through object-valued keys.
Params
o
: Object - Object to map throughfn
: function - Callback functiona
: Object - Accumulatorparams
: Object - Parameters object[.depth]
: number - Depth of reduce. Defaults to infinity[.iter]
: function - Iterator used by reduce. Defaults to inorder traversal.
Flat object
const o = { foo: "bar", bar: "baz" };
const combineVals = (a, [k, v]) => [...a, v];
obx.reduce(o, combineVals, []).join(", ");
// -> "bar, baz"
Deeper object
const o = {
foo: "bar",
bar: {
baz: "haz",
},
};
const combineVals = (a, [k, v]) => (v instanceof Object ? a : [...a, v]);
obx.reduce(o, combineVals, []).join(", ");
// -> "bar, haz"
Custom depth
const o = {
foo: "bar",
bar: {
baz: {
haz: "wow",
},
foo: "bar",
},
raz: {
faz: "maz",
gaz: 'haz',
haz: [
{ maz: 'waz' },
{ foo: 'moo' }
]
},
}
const combineVals = (a, [k, v]) => (v instanceof Object ? a : [...a, v]);
obx.reduce(o, combineVals, [], { depth: 2 }).join(", ");
// -> "bar, bar, maz, haz"
// Only gets keys down to depth 2
zip
Group multiple objects into a single iterator. Note that zip will also iterate through object-valued keys.
Params
objs
: Array - Array of objects to be zipped together.params
: Object - Parameters object[.depth]
: number - Depth of zip. Defaults to infinity[.key]
: Boolean - Whether zip should return object keys. Defaults tofalse
[.val]
: Boolean - Whether zip should return object values. Defaults totrue
[.last]
: Boolean - Whether zip should stop iterating when the last object is done, as opposed to the first. Defaults tofalse
[.iter]
: function - Iterator used by zip. Defaults to inorder traversal.
Stops at the first null value
const a = ["a", "b", "c"];
const b = [1];
// loop runs only once
for (const z of obx.zip([a, b]))
console.log(z)
// -> ["a", 1]
Recursive
const a = {
foo: "bar",
bar: {
baz: "haz",
},
};
const b = [4, 5];
for (const z of obx.zip([a, b]))
console.log(z)
// -> ["bar", 4]
// -> ["haz", 5]
More than 2 Objects
const a = ["a", "b", "c"];
const b = [1, 2, 3];
const c = ["x", "y", "z"];
const d = [3, 2, 1];
for (const z of obx.zip([a, b, c, d]))
console.log(z)
// -> ["a", 1, "x", 3]
// -> ["b", 2, "y", 2]
// -> ["c", 3, "z", 1]
sub
Recursive, in-place object subtraction.
Params
o
: Object - The object to be subtracted from. This object is mutated.s
: Object - The object to subtract withparams
: Object - Parameters object[.depth]
: number - Depth of subtraction. Defaults to infinity
Simple subtraction
const a = {
foo: "bar",
bar: "baz",
list: [1, 2, 3],
};
const b = {
foo: "bar",
list: [1, 2, 3],
};
obx.sub(a, b);
console.log(a)
// -> { bar: "baz" }
With arrays
const a = [1, 2, 3];
const b = [1, 2, 3];
obx.sub(a, b);
console.log(a)
// -> []
add
Recursive, in-place object addition. If both objects contain the same key, defaults to o
Params
o
: Object - The object to be added to. This object is mutated.a
: Object - The object to add withparams
: Object - Parameters object[.depth]
: number - Depth of addition. Defaults to infinity
Simple addition
const a = {
foo: "bar",
bar: "baz",
list: [1, 2, 3],
};
const b = {
foo: "bar",
haz: 5,
};
obx.add(a, b);
console.log(a)
// -> { foo: "bar", bar: "baz", list: [1, 2, 3], haz: 5 }
isEmptyObj
Assert that an object type is empty.
Params
o
: Object - Object to assert is empty
obx.isEmptyObj({}) // -> true
obx.isEmptyObj({ foo: 'bar' }) // -> false
Only works for objects
obx.isEmptyObj([]) // -> false
isEmptyArr
Assert that an array type is empty.
Params
a
: Array - The array to assert is empty
obx.isEmptyArr([]) // -> true
obx.isEmptyArr([1, 2, 3]) // -> false
Only works for arrays
obx.isEmptyArr({}) // -> false
Roadmap
- [x] Write docs
- [ ] Write
eq
in terms of a list of objects?- [ ] Rewrite
eq
using inorder iterator
- [ ] Rewrite
- [x] Implement
zip
- [ ] Create new file of opinionated functions with nicer signatures based on existing functions
- Other helpful functions like
isEmptyObj
andisEmptyArr
could go in there too
- Other helpful functions like
- [x] Add traversal options to
- [x]
reduce
- [x]
zip
- [x]
- [x] Complete test coverage
- [x] Test
add
- [x] Test
- [ ] Transition to TS
License
Distributed under the MIT License. See LICENSE.txt
for more information.
Contact
Twitter: @GaetanAlmela
Email: [email protected]
GitHub Repo: llGaetanll/obx