Home > Blockchain >  Object traversal function that goes over all attributes (including arrays w/o specified index) and r
Object traversal function that goes over all attributes (including arrays w/o specified index) and r

Time:11-05

I am trying to achieve a similar behavior to lodash's _.get() but with the difference that the function will be able to go over all/any of the arrays in the path instead of specifying the index in the path (ie. 'abc.xyz[0])


    // Example 1
    
    const _object = {abc: {xyz: [{value: 1}, {value: 2}]}};
    
    console.log(accumulateValues(_object, 'abc.xyz.value');
    // Desired output: [{value: 1}, {value: 2}]
    
    // Example 2
    
    const _object = {abc: [{xyz: [{values: 1}]}]};
    
    console.log(accumulateValues(_object, 'abc.xyz.values');
    // Desired output: [1]

CodePudding user response:

A fairly simple approach, if you want [1, 2] out of that might look like this:

const query = (path, ps = path .split ('.')) => (obj) =>
  ps .reduce ((a, p) => a .flatMap (x => (x || {}) [p]) .filter (Boolean), [obj])

const _object = {abc: [{xyz: [{value: 1}, {value: 2}]}]}

console .log (query ('abc.xyz.value') (_object))
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

If you're looking for [{value: 1}, {value: 2}], it probably wouldn't be much harder. But if somehow, as the question seems to imply, you want either result, based upon how you wrap 'abc', then I don't really understand your requirements.

Here the brackets surrounding some nodes (such as [xyz]) don't add any value, since we simply assume there is an array all the way down, wrapping the initial value in one. If you still wanted to supply them, we could remove them before processing:

const query = (path, ps = path .replace (/[\[\]]/g, '') .split ('.')) => (obj) =>
// ...

We might want to improve this to take arrays at the root by checking whether the input was an array already before wrapping it in one. I leave that as an exercise.

CodePudding user response:

Here is a solution using object-scan. It is a bit more flexible, you can e.g. match unknown attributes with * or use a regex to match segments and much more (see documentation).

// const objectScan = require('object-scan');

const f = (needle, obj) => objectScan([needle], {
  useArraySelector: false,
  rtn: 'value',
  reverse: false
})(obj);

const o1 = { abc: { xyz: [{ value: 1 }, { value: 2 }] } };
const r1a = f('abc.xyz', o1);
console.log(r1a);
// => [ { value: 1 }, { value: 2 } ]

const r1b = f('abc.xyz.value', o1);
console.log(r1b);
// => [ 1, 2 ]

const o2 = { abc: [{ xyz: [{ values: 1 }] }] };
const r2a = f('abc.xyz.values', o2);
console.log(r2a);
// => [ 1 ]

const r2b = f('abc.*.values', o2);
console.log(r2b);
// => [ 1 ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Disclaimer: I'm the author of object-scan

  • Related