Package Exports
- json-reduce
- json-reduce/dist/reduce.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (json-reduce) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
json-reduce
Reduce any JSON value by traversing depth first and visiting each node
Example: calculate the sum of all number values in a json object
import {reduce} from 'json-reduce'
const document = {
first: 1,
second: 2,
deep: {
array: [3, 4, 5, 6],
seven: 7
}
}
const result = reduce(
document,
(acc, value, path) => (typeof value === 'number' ? acc + value : acc),
0
)
console.log(result)
//=> 28
API
reduce(value, reducer, initialValue)
reducer
is the reducer function to execute for each node in the tree, and is given three arguments:
accumulator
- The accumulation of the callback's return values; it is the value returned from the previous invocation of the callback, orinitialValue
.value
- The current node being traversedpath
- The "dot-path" to the current node being traversed, e.g.['deep', 'array', 2]
Skipping subtrees
Sometimes when encountering a specific object or array value, you want to skip traversing the subtree. This can be done calling a provided SKIP function like this:
import reduce, {SKIP} from 'json-reduce'
const doc = {
species: [
{name: 'clover', type: 'plant'},
{name: 'trout', type: 'fish', eats: [{type: 'animal', name: 'crayfish'}]},
{
type: 'animal',
name: 'bear',
food: [
{
type: 'animal',
name: 'deer',
food: [{type: 'plant', name: 'leaves'}]
},
{
type: 'plant',
name: 'blueberry'
}
]
}
]
}
const result = reduce(
doc,
(acc, val, path) => {
if (val.type === 'plant' || val.type === 'fish') {
// We don't want to traverse the subtrees of these
return SKIP
}
// Collect all traversed paths
return acc.concat([path])
},
[]
)
expect(result).toEqual([
[],
['species'],
['species', 2],
['species', 2, 'type'],
['species', 2, 'name'],
['species', 2, 'food'],
['species', 2, 'food', 0],
['species', 2, 'food', 0, 'type'],
['species', 2, 'food', 0, 'name'],
['species', 2, 'food', 0, 'food']
])
Return and skip
In addition to return SKIP
, you can also call SKIP with a return value for convenience, to both return the accumulated
value and signal subtree skipping in one operation, e.g.:
//
reduce(doc, (acc, node) => {
if (node.type === 'plant' || node.type === 'fish') {
// Uppercase plant and fish names, but skip traversing subtrees
return SKIP(
acc.concat({
...node,
name: node.name.toUpperCase()
})
)
}
return acc
}, [])
Gotchas / Limitations
- Initial value is required. json-reduce does not work like Array.prototype.reduce with respect to missing initial value
- No circular reference detection and handling. Passing a circular structure to
reduce()
will probably crash with maximum call stack size exceeded. - Assumes a data structure that consists of valid JSON data types only. Traversal of
Map
,Set
, etc. are not currently supported (PR welcome!).