[](https://circleci.com/gh/blackflux/@micromint1npm/soluta-neque-eveniet) []
npm install @micromint1npm/soluta-neque-eveniet




Traverse object hierarchies using matching and callbacks.
Using npm:
$ npm i @micromint1npm/soluta-neque-eveniet
In a browser:
``html`
`js
import objectScan from '@micromint1npm/soluta-neque-eveniet';
const haystack = { a: { b: { c: 'd' }, e: { f: 'g' } } };
objectScan(['a.*.f'], { joined: true })(haystack);
// => [ 'a.e.f' ]
`
- Input traversed at most once during search
- Dependency free and tiny bundle size
- Powerful matching syntax
- Very performant
- Extensive tests and lots of examples
A needle expression specifies one or more paths to an element (or a set of elements) in a JSON structure.
Paths use the dot notation.
`txt`
store.book[0].title
The matching syntax is fully validated and bad input will throw a syntax error. The following syntax is supported:
- Array and Object matching
- Wildcard and Regex matching
- Or Clause
- Arbitrary Depth and Nested Path Recursion
- Exclusion
- Escaping
- Array Needles
Rectangular brackets for array path matching.
_Examples_:
['[2]'] (exact in array)
`js`
const haystack = [0, 1, 2, 3, 4];
objectScan(['[2]'], { joined: true })(haystack);
// => [ '[2]' ]
['[1]'] (no match in object)
`js`
const haystack = { 0: 'a', 1: 'b', 2: 'c' };
objectScan(['[1]'], { joined: true })(haystack);
// => []
Property name for object property matching.
_Examples_:
['foo'] (exact in object)
`js`
const haystack = { foo: 0, bar: 1 };
objectScan(['foo'], { joined: true })(haystack);
// => [ 'foo' ]
['1'] (no match in array)
`js`
const haystack = [0, 1, 2, 3, 4];
objectScan(['1'], { joined: true })(haystack);
// => []
The following characters have special meaning when not escaped:
- *: Match zero or more character+
- : Match one or more character?
- : Match exactly one character\
- : Escape the subsequent character
Can be used with Array and Object selector.
_Examples_:
['foo*'] (starting with foo)
`js`
const haystack = { foo: 0, foobar: 1, bar: 2 };
objectScan(['foo*'], { joined: true })(haystack);
// => [ 'foobar', 'foo' ]
['*'] (top level)
`js`
const haystack = { a: { b: 0, c: 1 }, d: 2 };
objectScan(['*'], { joined: true })(haystack);
// => [ 'd', 'a' ]
['[?5]'] (two digit ending in five)
`js`
const haystack = [...Array(30).keys()];
objectScan(['[?5]'], { joined: true })(haystack);
// => [ '[25]', '[15]' ]
['a.+.c'] (nested)
`js`
const haystack = { a: { b: { c: 0 }, d: { f: 0 } } };
objectScan(['a.+.c'], { joined: true })(haystack);
// => [ 'a.b.c' ]
['a.\\+.c'] (escaped)
`js`
const haystack = { a: { b: { c: 0 }, '+': { c: 0 } } };
objectScan(['a.\\+.c'], { joined: true })(haystack);
// => [ 'a.\\+.c' ]
Regex are defined by using parentheses.
Can be used with Array and Object selector.
_Examples_:
['(^foo)'] (starting with foo)
`js`
const haystack = { foo: 0, foobar: 1, bar: 2 };
objectScan(['(^foo)'], { joined: true })(haystack);
// => [ 'foobar', 'foo' ]5
['[(5)]'] (containing )
`js`
const haystack = [...Array(20).keys()];
objectScan(['[(5)]'], { joined: true })(haystack);
// => [ '[15]', '[5]' ][0]
['[(^[01]$)]'] ( and [1])
`js`
const haystack = ['a', 'b', 'c', 'd'];
objectScan(['[(^[01]$)]'], { joined: true })(haystack);
// => [ '[1]', '[0]' ][0]
['[(^[^01]$)]'] (other than and [1])
`js`
const haystack = ['a', 'b', 'c', 'd'];
objectScan(['[(^[^01]$)]'], { joined: true })(haystack);
// => [ '[3]', '[2]' ]
Or Clauses are defined by using curley brackets.
Can be used with Array and Object selector
and Arbitrary Depth matching.
_Examples_:
['[{0,1}]'] ([0] and [1])
`js`
const haystack = ['a', 'b', 'c', 'd'];
objectScan(['[{0,1}]'], { joined: true })(haystack);
// => [ '[1]', '[0]' ]a.b
['{a,d}.{b,f}'] (, a.f, d.b and d.f)
`js`
const haystack = { a: { b: 0, c: 1 }, d: { e: 2, f: 3 } };
objectScan(['{a,d}.{b,f}'], { joined: true })(haystack);
// => [ 'd.f', 'a.b' ]
There are two types of arbitrary depth matching:
- **: Matches zero or more nestings++
- : Matches one or more nestings
Can be combined with Regex and Or Clause by prepending.
_Examples_:
['a.**'] (zero or more nestings under a)
`js`
const haystack = { a: { b: 0, c: 0 } };
objectScan(['a.**'], { joined: true })(haystack);
// => [ 'a.c', 'a.b', 'a' ]a
['a.++'] (one or more nestings under )
`js`
const haystack = { a: { b: 0, c: 0 } };
objectScan(['a.++'], { joined: true })(haystack);
// => [ 'a.c', 'a.b' ]1
['**(1)'] (all containing at every level)
`js`
const haystack = { 1: { 1: ['c', 'd'] }, 510: 'e', foo: { 1: 'f' } };
objectScan(['**(1)'], { joined: true })(haystack);
// => [ '510', '1.1[1]', '1.1', '1' ]
To match a nested path recursively,
combine Arbitrary Depth matching with an Or Clause.
There are two types of nested path matching:
- **{...}: Matches path(s) in Or Clause zero or more times++{...}
- : Matches path(s) in Or Clause one or more times
_Examples_:
['++{[0][1]}'] (cyclic path)
`js`
const haystack = [[[[0, 1], [1, 2]], [[3, 4], [5, 6]]], [[[7, 8], [9, 10]], [[11, 12], [13, 14]]]];
objectScan(['++{[0][1]}'], { joined: true })(haystack);
// => [ '[0][1][0][1]', '[0][1]' ]nested or
['++{[0],[1]}'] ()
`js`
const haystack = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];
objectScan(['++{[0],[1]}'], { joined: true })(haystack);
// => [ '[1][1]', '[1][0]', '[1]', '[0][1]', '[0][0]', '[0]' ]traverse only array
['*{[]}'] ()
`js`
const haystack = [[[{ a: [1] }], [2]]];
objectScan(['*{[]}'], { joined: true })(haystack);
// => [ '[0][1][0]', '[0][1]', '[0][0][0]', '[0][0]', '[0]' ]traverse only object
['*{}'] ()
`js`
const haystack = { a: [0, { b: 1 }], c: { d: 2 } };
objectScan(['*{}'], { joined: true })(haystack);
// => [ 'c.d', 'c', 'a' ]zero or more times
['a.**{b.c}'] ()
`js`
const haystack = { a: { b: { c: { b: { c: 0 } } } } };
objectScan(['a.**{b.c}'], { joined: true })(haystack);
// => [ 'a.b.c.b.c', 'a.b.c', 'a' ]one or more times
['a.++{b.c}'] ()
`js`
const haystack = { a: { b: { c: { b: { c: 0 } } } } };
objectScan(['a.++{b.c}'], { joined: true })(haystack);
// => [ 'a.b.c.b.c', 'a.b.c' ]
To exclude a path, use exclamation mark.
_Examples_:
['{a,b},!a'] (only b)
`js`
const haystack = { a: 0, b: 1 };
objectScan(['{a,b},!a'], {
joined: true,
strict: false
})(haystack);
// => [ 'b' ]a
[',!.a'] (all except ending in )
`js`
const haystack = { a: 0, b: { a: 1, c: 2 } };
objectScan([',!.a'], { joined: true })(haystack);
// => [ 'b.c', 'b' ]
['[*]', '[!(^[01]$)]'] (exclude with regex)
`js`
const haystack = ['a', 'b', 'c', 'd'];
objectScan(['[*]', '[!(^[01]$)]'], { joined: true })(haystack);
// => [ '[3]', '[2]' ]
The following characters are considered special and need to
be escaped using \, if they should be matched in a key:[, ], {, }, (, ), ,, ., !, ?, *, + and \.
_Examples:_
['\\[1\\]'] (special object key)
`js`
const haystack = { '[1]': 0 };
objectScan(['\\[1\\]'], { joined: true })(haystack);
// => [ '\\[1\\]' ]
Needles can be passed as arrays, consisting of integers and strings.
When given as arrays, then needles:
- match array keys with integers and object keys with strings
- do not support any other matching syntax
- do not require escaping
- parse faster than regular string needles
This syntax allows for key result of @micromint1npm/soluta-neque-eveniet to be passed back into itself.
Be advised that matchedBy and similar contain the original needles and not copies.
Array needles work similarly to how they work in _.get.
_Examples:_
[['a', 0, 'b']] (mixed path)
`js`
const haystack = { a: [{ b: 0 }] };
objectScan([['a', 0, 'b']], { joined: true })(haystack);
// => [ 'a[0].b' ]
[['a.b', 0]] (implicit escape)
`js`
const haystack = { 'a.b': [0], a: { b: [1] } };
objectScan([['a.b', 0]], {
joined: true,
rtn: 'value'
})(haystack);
// => [ 0 ]
[['a', 0, 'b'], ['a', 1, 'b'], 'a[*].b'] (mixed needles)
`js`
const haystack = { a: [{ b: 0 }, { b: 0 }] };
objectScan([['a', 0, 'b'], ['a', 1, 'b'], 'a[*].b'], {
joined: true,
rtn: 'matchedBy'
})(haystack);
// => [ [ [ 'a', 1, 'b' ], 'a[].b' ], [ [ 'a', 0, 'b' ], 'a[].b' ] ]
[['a', 'b']] (useArraySelector=false)
`js`
const haystack = { a: [{ b: 0 }, { b: 0 }] };
objectScan([['a', 'b']], {
joined: true,
useArraySelector: false
})(haystack);
// => [ 'a[1].b', 'a[0].b' ]
Fn({ key, value, ... })
where:
- key: key that callback is invoked for (respects joined option).value
- : value for key.entry
- : entry consisting of [key, value].property
- : current parent property.gproperty
- : current grandparent property.parent
- : current parent.gparent
- : current grandparent.parents
- : array of form [parent, grandparent, ...].isMatch
- : true iff last targeting needle exists and is non-excluding.matchedBy
- : all non-excluding needles targeting key.excludedBy
- : all excluding needles targeting key.traversedBy
- : all needles involved in traversing key.isCircular
- : true iff value contained in parentsisLeaf
- : true iff value can not be traverseddepth
- : length of keyresult
- : intermittent result as defined by rtngetKey(joined?: boolean)
- : function that returns keygetValue
- : function that returns valuegetEntry(joined?: boolean)
- : function that returns entrygetProperty
- : function that returns propertygetGproperty
- : function that returns gpropertygetParent
- : function that returns parentgetGparent
- : function that returns gparentgetParents
- : function that returns parentsgetIsMatch
- : function that returns isMatchgetMatchedBy
- : function that returns matchedBygetExcludedBy
- : function that returns excludedBygetTraversedBy
- : function that returns traversedBygetIsCircular
- : function that returns isCirculargetIsLeaf
- : function that returns isLeafgetDepth
- : function that returns depthgetResult
- : function that returns resultcontext
- : as passed into the search
_Notes on Performance_
- Arguments backed by getters use Functions Getter
and should be accessed via destructuring to prevent redundant computation.
- Getters should be used to improve performance for conditional access. E.g. if (isMatch) { getParents() ... }.
- For performance reasons, the same object is passed to all callbacks.
_Search Context_
- A context can be passed into a search invocation as a second parameter. It is available in all callbacks
and can be used to manage state across a search invocation without having to recompile the search.
- By default, all matched keys are returned from a search invocation.
However, when it is not _undefined_, the context is returned instead.
_Examples_:
['**.{c,d,e}'] (search context)
`js`
const haystack = { a: { b: { c: 2, d: 11 }, e: 7 } };
objectScan(['**.{c,d,e}'], {
joined: true,
filterFn: ({ value, context }) => { context.sum += value; }
})(haystack, { sum: 0 });
// => { sum: 20 }
Type: functionundefined
Default:
When defined, this callback is invoked for every match. If false
is returned, the current key is excluded from the result.
The return value of this callback has no effect when a search context is provided.
Can be used to do processing as matching keys are traversed.
Invoked in same order as matches would appear in result.
For more information on invocation order, please refer to Section Traversal Order.
This method is conceptually similar to
Array.filter().
_Examples_:
['**'] (filter function)
`js`
const haystack = { a: 0, b: 'bar' };
objectScan(['**'], {
joined: true,
filterFn: ({ value }) => typeof value === 'string'
})(haystack);
// => [ 'b' ]
Type: functionundefined
Default:
When defined, this callback is invoked for every key that is traversed by
the search. If true is returned, all keys nested under the current key are
skipped in the search and from the final result.
Note that breakFn is invoked before the corresponding filterFn might be invoked.
For more information on invocation order, please refer to Section Traversal Order.
_Examples_:
['**'] (break function)
`js`
const haystack = { a: { b: { c: 0 } } };
objectScan(['**'], {
joined: true,
breakFn: ({ key }) => key === 'a.b'
})(haystack);
// => [ 'a.b', 'a' ]
Type: functionundefined
Default:
When defined, this function is called before traversal as beforeFn(state = { haystack, context }).
If a value other than undefined is returned from beforeFn,state.haystack
that value is written to before traversal.
The content of state can be modified in the function.beforeFn
After has executed, the traversal happens using state.haystack and state.context.
The content in state can be accessed in afterFn.result
Note however that the key is being overwritten.
_Examples_:
['**'] (combining haystack and context)
`js`
const haystack = { a: 0 };
objectScan(['**'], {
joined: true,
beforeFn: ({ haystack: h, context: c }) => [h, c],
rtn: 'key'
})(haystack, { b: 0 });
// => [ '[1].b', '[1]', '[0].a', '[0]' ]
['**'] (pre-processing haystack)
`js`
const haystack = { a: 0, b: 1 };
objectScan(['**'], {
joined: true,
beforeFn: ({ haystack: h }) => Object.keys(h),
rtn: ['key', 'value']
})(haystack);
// => [ [ '[1]', 'b' ], [ '[0]', 'a' ] ]
Type: functionundefined
Default:
When defined, this function is called after traversal as afterFn(state = { result, haystack, context }).
Additional information written to state in beforeFn is available in afterFn.
The content of state can be modified in the function. In particular the key state.result can be updated.
If a value other than undefined is returned from afterFn, that value is written to state.result.
After beforeFn has executed, the key state.result is returned as the final result.
_Examples_:
['**'] (returning count plus context)
`js`
const haystack = { a: 0 };
objectScan(['**'], {
afterFn: ({ result, context }) => result + context,
rtn: 'count'
})(haystack, 5);
// => 6
['**'] (post-processing result)
`js`
const haystack = { a: 0, b: 3, c: 4 };
objectScan(['**'], {
afterFn: ({ result }) => result.filter((v) => v > 3),
rtn: 'value'
})(haystack);
// => [ 4 ]
['**'] (pass data from beforeFn to afterFn)
`js`
const haystack = {};
objectScan(['**'], {
beforeFn: (state) => { / eslint-disable no-param-reassign / state.custom = 7; },
afterFn: (state) => state.custom
})(haystack);
// => 7
Type: functionundefined
Default:
This function has the same signature as the callback functions. When defined it is expected to return a function or undefined.
The returned value is used as a comparator to determine the traversal order of any object keys.
This works together with the reverse option.
Please refer to Section Traversal Order for more information.
_Examples_:
['**'] (simple sort)
`js`
const haystack = { a: 0, c: 1, b: 2 };
objectScan(['**'], {
joined: true,
compareFn: () => (k1, k2) => k1.localeCompare(k2),
reverse: false
})(haystack);
// => [ 'a', 'b', 'c' ]
Type: booleantrue
Default:
When set to true, the traversal is performed in reverse order. This means breakFn is executed in _reverse post-order_ andfilterFn in _reverse pre-order_. Otherwise breakFn is executed in _pre-order_ and filterFn in _post-order_.
When reverse is true the traversal is _delete-safe_. I.e. property can be deleted / spliced from parent object / array in filterFn.
Please refer to Section Traversal Order for more information.
_Examples_:
['**'] (breakFn, reverse true)
`js`
const haystack = { f: { b: { a: {}, d: { c: {}, e: {} } }, g: { i: { h: {} } } } };
objectScan(['**'], {
breakFn: ({ isMatch, property, context }) => { if (isMatch) { context.push(property); } },
reverse: true
})(haystack, []);
// => [ 'f', 'g', 'i', 'h', 'b', 'd', 'e', 'c', 'a' ]
['**'] (filterFn, reverse true)
`js`
const haystack = { f: { b: { a: {}, d: { c: {}, e: {} } }, g: { i: { h: {} } } } };
objectScan(['**'], {
filterFn: ({ property, context }) => { context.push(property); },
reverse: true
})(haystack, []);
// => [ 'h', 'i', 'g', 'e', 'c', 'd', 'a', 'b', 'f' ]
['**'] (breakFn, reverse false)
`js`
const haystack = { f: { b: { a: {}, d: { c: {}, e: {} } }, g: { i: { h: {} } } } };
objectScan(['**'], {
breakFn: ({ isMatch, property, context }) => { if (isMatch) { context.push(property); } },
reverse: false
})(haystack, []);
// => [ 'f', 'b', 'a', 'd', 'c', 'e', 'g', 'i', 'h' ]
['**'] (filterFn, reverse false)
`js`
const haystack = { f: { b: { a: {}, d: { c: {}, e: {} } }, g: { i: { h: {} } } } };
objectScan(['**'], {
filterFn: ({ property, context }) => { context.push(property); },
reverse: false
})(haystack, []);
// => [ 'a', 'c', 'e', 'd', 'b', 'h', 'i', 'g', 'f' ]
Type: booleanfalse
Default:
When set to false, all targeted keys are traversed and matchedcompareFn
in the order determined by the and reverse option.
When set to true, all targeted keys are traversed and matched
in the order determined by the corresponding needles,
falling back to the above ordering.
Note that this option is constraint by the depth-first search approach.
_Examples_:
['c', 'a', 'b'] (order by needle)
`js`
const haystack = { a: 0, b: 1, c: 1 };
objectScan(['c', 'a', 'b'], {
joined: true,
orderByNeedles: true
})(haystack);
// => [ 'c', 'a', 'b' ]
['b', '*'] (fallback reverse)
`js`
const haystack = { a: 0, b: 1, c: 1 };
objectScan(['b', '*'], {
joined: true,
reverse: true,
orderByNeedles: true
})(haystack);
// => [ 'b', 'c', 'a' ]
['b', '*'] (fallback not reverse)
`js`
const haystack = { a: 0, b: 1, c: 1 };
objectScan(['b', '*'], {
joined: true,
reverse: false,
orderByNeedles: true
})(haystack);
// => [ 'b', 'a', 'c' ]
['a', 'b.c', 'd'] (nested match)
`js`
const haystack = { a: 0, b: { c: 1 }, d: 2 };
objectScan(['a', 'b.c', 'd'], {
joined: true,
orderByNeedles: true
})(haystack);
// => [ 'a', 'b.c', 'd' ]
['b', 'a', 'b.c', 'd'] (matches traverse first)
`js`
const haystack = { a: 0, b: { c: 1 }, d: 2 };
objectScan(['b', 'a', 'b.c', 'd'], {
joined: true,
orderByNeedles: true
})(haystack);
// => [ 'b.c', 'b', 'a', 'd' ]
Type: booleanfalse
Default:
When set to true the traversal immediately returns after the first match.
_Examples_:
['a', 'b'] (only return first property)
`js`
const haystack = { a: 0, b: 1 };
objectScan(['a', 'b'], {
rtn: 'property',
abort: true
})(haystack);
// => 'b'
['[0]', '[1]'] (abort changes count)
`js`
const haystack = ['a', 'b'];
objectScan(['[0]', '[1]'], {
rtn: 'count',
abort: true
})(haystack);
// => 1
Type: string or array or function
Default: _dynamic_
Defaults to key when search context is _undefined_ and to context otherwise.
Can be explicitly set as a string:context
- : search context is returnedkey
- : as passed into filterFnvalue
- : as passed into filterFnentry
- : as passed into filterFnproperty
- : as passed into filterFngproperty
- : as passed into filterFnparent
- : as passed into filterFngparent
- : as passed into filterFnparents
- : as passed into filterFnisMatch
- : as passed into filterFnmatchedBy
- : as passed into filterFnexcludedBy
- : as passed into filterFntraversedBy
- : as passed into filterFnisCircular
- : as passed into filterFnisLeaf
- : as passed into filterFndepth
- : as passed into filterFnbool
- : returns _true_ iff a match is foundcount
- : returns the match countsum
- : returns the match sum
When set to array, can contain any of the above except context, bool, count and sum.
When set to function, called with _callback_ signature for every match. Returned value is added to the result.
When abort is set to true and _rtn_ is not context, bool, count or sum,
the first entry of the result or _undefined_ is returned.
_Examples_:
['[*]'] (return values)
`js`
const haystack = ['a', 'b', 'c'];
objectScan(['[*]'], { rtn: 'value' })(haystack);
// => [ 'c', 'b', 'a' ]
['foo[*]'] (return entries)
`js`
const haystack = { foo: ['bar'] };
objectScan(['foo[*]'], { rtn: 'entry' })(haystack);
// => [ [ [ 'foo', 0 ], 'bar' ] ]
['a.b.c', 'a'] (return properties)
`js`
const haystack = { a: { b: { c: 0 } } };
objectScan(['a.b.c', 'a'], { rtn: 'property' })(haystack);
// => [ 'c', 'a' ]
['a.b', 'a.c'] (checks for any match, full traversal)
`js`
const haystack = { a: { b: 0, c: 1 } };
objectScan(['a.b', 'a.c'], { rtn: 'bool' })(haystack);
// => true
['**'] (return not provided context)
`js`
const haystack = { a: 0 };
objectScan(['**'], { rtn: 'context' })(haystack);
// => undefined
['a.b.{c,d}'] (return keys with context passed)
`js`
const haystack = { a: { b: { c: 0, d: 1 } } };
objectScan(['a.b.{c,d}'], { rtn: 'key' })(haystack, []);
// => [ [ 'a', 'b', 'd' ], [ 'a', 'b', 'c' ] ]
['a.b.{c,d}'] (return custom array)
`js`
const haystack = { a: { b: { c: 0, d: 1 } } };
objectScan(['a.b.{c,d}'], { rtn: ['property', 'value'] })(haystack, []);
// => [ [ 'd', 1 ], [ 'c', 0 ] ]
['**'] (return value plus one)
`js`
const haystack = { a: { b: { c: 0, d: 1 } } };
objectScan(['**'], {
filterFn: ({ isLeaf }) => isLeaf,
rtn: ({ value }) => value + 1
})(haystack);
// => [ 2, 1 ]
['**'] (return sum)
`js`
const haystack = { a: { b: { c: -2, d: 1 }, e: [3, 7] } };
objectScan(['**'], {
filterFn: ({ value }) => typeof value === 'number',
rtn: 'sum'
})(haystack);
// => 9
Type: booleanfalse
Default:
Keys are returned as a string when set to true instead of as a list.
Setting this option to true will negatively impact performance.
This setting can be overwritten by using the getter method getKey() or getEntry().
Note that _.get and _.set fully support lists.
_Examples_:
['[]', '[].foo'] (joined)
`js`
const haystack = [0, 1, { foo: 'bar' }];
objectScan(['[]', '[].foo'], { joined: true })(haystack);
// => [ '[2].foo', '[2]', '[1]', '[0]' ]
['[]', '[].foo'] (not joined)
`js`
const haystack = [0, 1, { foo: 'bar' }];
objectScan(['[]', '[].foo'])(haystack);
// => [ [ 2, 'foo' ], [ 2 ], [ 1 ], [ 0 ] ]
['**.c'] (joined, getKey)
`js`
const haystack = { a: { b: { c: 0 } } };
objectScan(['**.c'], {
joined: true,
rtn: ({ getKey }) => [getKey(true), getKey(false), getKey()]
})(haystack);
// => [ [ 'a.b.c', [ 'a', 'b', 'c' ], 'a.b.c' ] ]
['**.c'] (not joined, getEntry)
`js`
const haystack = { a: { b: { c: 0 } } };
objectScan(['**.c'], { rtn: ({ getEntry }) => [getEntry(true), getEntry(false), getEntry()] })(haystack);
// => [ [ [ 'a.b.c', 0 ], [ [ 'a', 'b', 'c' ], 0 ], [ [ 'a', 'b', 'c' ], 0 ] ] ]
Type: booleantrue
Default:
When set to false, no array selectors should be used in any needles and arrays are automatically traversed.
Note that the results still include the array selectors.
_Examples_:
['a', 'b.d'] (automatic array traversal)
`js`
const haystack = [{ a: 0 }, { b: [{ c: 1 }, { d: 2 }] }];
objectScan(['a', 'b.d'], {
joined: true,
useArraySelector: false
})(haystack);
// => [ '[1].b[1].d', '[0].a' ]
[''] (top level array matching)
`js`
const haystack = [{ a: 0 }, { b: 1 }];
objectScan([''], {
joined: true,
useArraySelector: false
})(haystack);
// => [ '[1]', '[0]' ]
Type: booleantrue
Default:
When set to true, errors are thrown when:
- a path is identical to a previous path
- a path invalidates a previous path
- a path contains consecutive recursions
_Examples_:
['a.b', 'a.b'] (identical)
`js`
const haystack = [];
objectScan(['a.b', 'a.b'], { joined: true })(haystack);
// => 'Error: Redundant Needle Target: "a.b" vs "a.b"'
['a.{b,b}'] (identical, same needle)
`js`
const haystack = [];
objectScan(['a.{b,b}'], { joined: true })(haystack);
// => 'Error: Redundant Needle Target: "a.{b,b}" vs "a.{b,b}"'
['a.b', 'a.**'] (invalidates previous)
`js`
const haystack = [];
objectScan(['a.b', 'a.**'], { joined: true })(haystack);
// => 'Error: Needle Target Invalidated: "a.b" by "a.**"'
['.!'] (consecutive recursion)
`js`
const haystack = [];
objectScan(['.!'], { joined: true })(haystack);
// => 'Error: Redundant Recursion: ".!"'
This library has a similar syntax and can perform similar tasks
to jsonpath or jmespath.
But instead of querying an object hierarchy, it focuses on traversing it.
Hence, it is designed around handling multiple paths in a single traversal.
_No other library doing this is currently available._
While nimma provides the ability to traverse multiple paths,
it doesn't do it in a single traversal.
A one-to-one comparison with other libraries is difficult due to difference in functionality,
but it can be said that @micromint1npm/soluta-neque-eveniet` is more versatile at similar performance.
| |objectScan (compiled)|objectScan|nimma (compiled)|nimma|jsonpath-plus|jsonpath|jmespath|
|---|---|---|---|---|---|---|---|
|Get Key|||||||-|
|Get Value||||||||
|Conditional Path| [1]| [1]||||||
|Recursive Traversal| [2]| [2]| [3]| [3]|![](https://img.shields.io/badge/2.63x-6c961c?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAN5QTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA