Better way to map a deep object to new object

2019-08-04 01:11发布

问题:

This code works for converting the JSON to an object where each name object turns into the key for either its value, or if it instead has its own element object breaks that out and does the same to its contents.

Is there a better way to do this that would also allow for more extensiblity of the JSON schema?
Is there a way I can get it all down to a simpler function that I can pass the first element and have it convert it down to whatever depth the schema goes?

const fs = require('fs');
{
    let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version='1.0'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
    let depth = 0;

    var compiled = {
        [scheme.ele.name]: scheme.ele.ele.map(function(i) {
            if (typeof i.ele != 'undefined') {
                return {
                    [i.name]: i.ele.map(function(k) {
                        if (typeof k.ele != 'undefined') {
                            return {
                                [k.name]: k.ele.map(function(p) {
                                    if (typeof p.ele != 'undefined') {
                                        return {
                                            [p.name]: p.ele
                                        };
                                    } else {
                                        return {
                                            [p.name]: p.value
                                        };
                                    }
                                })
                            };
                        } else {
                            return {
                                [k.name]: k.value
                            };
                        }
                    })
                };
            } else {
                return {
                    [i.name]: i.value
                };
            }
        })
    };
}

console.log(JSON.stringify(compiled, 0, 2));

I should add, this is intended to eventually also apply validation and grab real data when it gets to the string objects.

The output looks like this:

{
    "REPORT": [
    {
        "SEGMENT0": [
        {
            "NUMBER1": ""
        },
        {
            "NUMBER2": ""
        }
        ]
    },
    {
        "SEGMENT1": [
        {
            "RECORD1": [
            {
                "NUMBER1": ""
            },
            {
                "NUMBER2": ""
            }
            ]
        }
        ]
    },
    {
        "SEGMENT2": []
    },
    {
        "SEGMENT3": []
    },
    {
        "SEGMENT4": []
    },
    {
        "SEGMENT5": []
    }
    ]
}

回答1:

Nina's answer is cleaner but this looks a bit more like your code so I figured I'd post it anyway.

let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":"1"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"2"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let newScheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":"1"},{"name":"NUMBER2","value":"3"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"4"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;

//Yay, recursion!
function mapObj(a, o = {}) {

  let array = o[a.name] || [];

  for (let i = 0; i < a.ele.length; i++) {

    let b = a.ele[i];
    array[i] = b.ele ?
      mapObj(b, array[i]) : {
        [b.name]: b.value
      };
  }
  
  o[a.name] = array;
  return o;

}

let obj = mapObj(scheme.ele);

console.log(obj);
console.log(mapObj(newScheme.ele, obj));



回答2:

You could destructure the object, get name, ele and value and return a new object with name as key and either an array by mapping the objects of ele or the value.

const
    getData = ({ name, ele, value }) => ({
        [name]: Array.isArray(ele)
            ? ele.map(getData)
            : value
    });

var scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0\'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root,
    result = getData(scheme.ele);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }