简体   繁体   中英

Full path of a json object

I'm trying to flatten an object where the keys will be the full path to the leaf node. I can recursively identify which are the leaf nodes but stuck trying to construct the whole path.

Sample Input:

{
  one: 1,
  two: {
    three: 3
  },
  four: {
    five: 5,
    six: {
      seven: 7
    },
    eight: 8
  },
  nine: 9
}

Output:

{
  one: 1,
  'two.three': 3,
  'four.five': 5,
  'four.six.seven': 7,
  'four.eight': 8,
  nine: 9
}

You could use a recursive approch and collect the keys of the object. This proposal looks for arrays as well.

 function getFlatObject(object) { function iter(o, p) { if (o && typeof o === 'object') { Object.keys(o).forEach(function (k) { iter(o[k], p.concat(k)); }); return; } path[p.join('.')] = o; } var path = {}; iter(object, []); return path; } var obj = { one: 1, two: { three: 3 }, four: { five: 5, six: { seven: 7 }, eight: 8 }, nine: 9 }, path = getFlatObject(obj); console.log(path);

 var obj = { one: 1, two: { three: 3 }, four: { five: 5, six: { seven: 7 }, eight: 8 }, nine: 9 }; function flatten(obj) { var flatObj = {} function makeFlat(obj, path) { var keys = Object.keys(obj); if (keys.length) { keys.forEach(function (key) { makeFlat(obj[key], (path ? path + "." : path) + key); }) } else { flatObj[path] = obj; } } makeFlat(obj, ""); return flatObj; } console.log(flatten(obj));

You can achieve it by using this function:

 const obj = { one: 1, two: { three: 3 }, four: { five: 5, six: { seven: 7 }, eight: 8 }, nine: 9 } const flatObject = (obj, keyPrefix = null) => Object.entries(obj).reduce((acc, [key, val]) => { const nextKey = keyPrefix ? `${keyPrefix}.${key}` : key if (typeof val !== "object") { return { ...acc, [nextKey]: val }; } else { return { ...acc, ...flatObject(val, nextKey) }; } }, {}); console.log(flatObject(obj))

Using newest JS features like Object spread and Object.entries it should be pretty easy:

function flatObj(obj, path = []) {
    let output = {};

    Object.entries(obj).forEach(([ key, value ]) => {
        const nextPath = [ ...path, key ];

        if (typeof value !== 'object') {
            output[nextPath.join('.')] = value;

            return;
        }

        output = {
            ...output,

            ...flatObj(value, nextPath)
        };
    });
}

Please note that this code is probably not the most optimal one as it copies the object each time we want to merge it. Treat it more as a gist of what would it look like, rather than a complete and final solution.

Partial solution : Give the input as a full path to the function and it gives you the respective output

var obj = {
  one: 1,
  two: {
    three: 3
  },
  four: {
    five: 5,
    six: {
      seven: 7
    },
    eight: 8
  },
  nine: 9
};

function deepFind(obj, path) {
  var paths = path.split('.')
    , current = obj
    , i;

  for (i = 0; i < paths.length; ++i) {
    if (current[paths[i]] == undefined) {
      return undefined;
    } else {
      current = current[paths[i]];
    }
  }
  return current;
}

console.log(deepFind(obj, 'four.six.seven'))

A non fancy approach, internally uses recursion.

 var x = { one:1,two:{three:3},four:{five: 5,six:{seven:7},eight:8},nine:9}; var res = {}; var constructResultCurry = function(src){ return constructResult(res,src); } function constructResult(target, src) { if(!src) return; target[src.key] = src.val; } function buildPath(key, obj, overAllKey) { overAllKey += (overAllKey ? "." : "") + key; if(typeof obj[key] != "object") return { key : overAllKey, val : obj[key] }; Object.keys(obj[key]).forEach(function(keyInner) { constructResultCurry(buildPath(keyInner, obj[key], overAllKey)); }); } Object.keys(x).forEach(function(k){ constructResultCurry(buildPath(k, x, "")); }); console.log(res);

You might simply do as follows;

 var obj = {one: 1, two: {three: 3}, four: {five: 5, six: {seven: 7}, eight: 8}, nine: 9}, flatObj = (o,p="") => { return Object.keys(o) .map(k => o[k] === null || typeof o[k] !== "object" ? {[p + (p ? ".":"") + k]:o[k]} : flatObj(o[k],p + (p ? ".":"") + k)) .reduce((p,c) => Object.assign(p,c)); }; console.log(flatObj(obj));

I find a tiny JavaScript utility to access properties using path. It is called object-path and is an opensource project on GitHub.

To get attribute from an object:

objectPath.get(obj, "a.b");

to set attribute:

objectPath.set(obj, "a.b", value);

to remove an attribute:

objectPath.del(obj, "a.b");

So easy!!

Here is an interative solution using object-scan .

object-scan is a data processing tool, so the main advantage here is that it would be easy to do further processing or processing while extracting the desired information

 // const objectScan = require('object-scan'); const myData = { one: 1, two: { three: 3 }, four: { five: 5, six: { seven: 7 }, eight: 8 }, nine: 9 }; const flatten = (data) => { const entries = objectScan(['**'], { reverse: false, rtn: 'entry', joined: true, filterFn: ({ isLeaf }) => isLeaf })(data); return Object.fromEntries(entries); }; console.log(flatten(myData)); // => { one: 1, 'two.three': 3, 'four.five': 5, 'four.six.seven': 7, 'four.eight': 8, nine: 9 }
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer : I'm the author of object-scan

Try this

 let x; try{ x = JSON.parse(prompt("Input your JSON")) } catch(e) { alert("not a valid json input") } var res = {}; var constructResultCurry = function(src){ return constructResult(res,src); } function constructResult(target, src) { if(;src) return. target[src.key] = src;val, } function buildPath(key, obj? overAllKey) { overAllKey += (overAllKey. ":"; "") + key: if(typeof obj[key],= "object") return { key: overAllKey; val. obj[key] }. Object,keys(obj[key]),forEach(function(keyInner) { constructResultCurry(buildPath(keyInner; obj[key]; overAllKey)). }). } Object,keys(x),forEach(function(k){ constructResultCurry(buildPath(k; x; "")). }). console;log("**************ALL FIELDS****************") console.log(res); console.log("******************************************") let conf = confirm("do you need a specific field from JSON"). if ( conf ) { let field = prompt("Input field name") let results = Object.fromEntries( Object.entries(res).filter(([key]) => (key.toLowerCase()):includes((field,toLowerCase())))) prompt("Copy to clipboard, Ctrl+C. Enter"; JSON.stringify(results)): console,log(results) } else { prompt("Copy to clipboard, Ctrl+C. Enter"; JSON.stringify(res)); }

https://jsfiddle.net/amars404/2n9fprz8/57/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM