Table of Contents
obx is a super fast, tiny, well documented, and battle-tested object manipulation library for Javascript.
Install with npm
npm i @almela/obx
or with yarn
yarn add @almela/obx
In your file, simply add
import * as obx from '@almela/obx'
or simply import select functions
import { eq, cp } from '@almela/obx'
For even more examples, see the tests.
- 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)
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
Examples
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
Deep copy an object.
Params
o
:Object
- Object to copyparams
:Object
- Parameters object[.depth]
:number
- Depth of copy. Defaults to infinity
Examples
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 value from object.
Params
o
:Object
- The objectp
:String
- Value path
Examples
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 value in object.
Params
o
:Object
- The object to be mutatedp
:String
- Value pathv
:Object
- Value to set
Examples
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'
// }]
// }
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
Examples
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
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
Examples
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!",
// }
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.
Examples
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
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.
Examples
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]
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
Examples
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)
// -> []
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
Examples
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 }
Assert that an object type is empty.
Params
o
:Object
- Object to assert is empty
Examples
obx.isEmptyObj({}) // -> true
obx.isEmptyObj({ foo: 'bar' }) // -> false
Only works for objects
obx.isEmptyObj([]) // -> false
Assert that an array type is empty.
Params
a
:Array
- The array to assert is empty
Examples
obx.isEmptyArr([]) // -> true
obx.isEmptyArr([1, 2, 3]) // -> false
Only works for arrays
obx.isEmptyArr({}) // -> false
- Write docs
- Write
eq
in terms of a list of objects?- Rewrite
eq
using inorder iterator
- Rewrite
- 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
- Add traversal options to
-
reduce
-
zip
-
- Complete test coverage
- Test
add
- Test
- Transition to TS
Distributed under the MIT License. See LICENSE.txt
for more information.
Twitter: @GaetanAlmela
Email: npm@almela.io
GitHub Repo: llGaetanll/obx