Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
147 views
in Technique[技术] by (71.8m points)

javascript - Arrange JSON Array to given Hierarchy group without Parent ID

I have an Array of Key names (let's call it 'Labels') and another array of Objects (let's call it 'Data'). I am trying to convert Data Array into Parent Child structure by using the hierarchy of Labels Array. That means, First Data will be grouped by "Gender" (parent) and then "Age" (children of Gender) and then "ID" (children of Age)

//.... means it can be any number of items
var Labels = ["Gender", "Age", "ID", ....];
var Data = [{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
}]

Expected Output of above Data should be:

var output = [{
            name: "Male",
            childrens:[
            {
                name: "21",
                childrens: [{name: "Al01"},{name: "Al07"}, ....]
            },
            {
                name: "22",
                childrens: [{name: "A111"},{name: "A113"}, ....]
            },
            {
                name: "23",
                childrens: [{name: "A109"},{name: "A115"}, ....]
            }
            ]
        },
        {
            name: "Female",
            childrens:[
            {
                name: "21",
                childrens: [{name: "Al04"},{name: "Al10"}, ....]
            },
            {
                name: "22",
                childrens: [{name: "A102"},{name: "A114"}, ....]
            },
            {
                name: "23",
                childrens: [{name: "A106"},{name: "A109"}, ....]
            }
            ]
        }];

I've tried groupBy() as shown below But not getting the expected output.

var groupBy = function (xs, key) {
        return xs.reduce(function (rv, x) {
            (rv[x[key]] = rv[x[key]] || []).push(x);
            return rv;
        }, {});
    };
function calcCats(ca, ss) {
        let K2 = groupBy(ca, ss);
        ca = [];
        Object.keys(K2).forEach(cc => {
            K2[cc].forEach(kk=>{
                delete kk[ss];
            });
            let tv = { name: cc, type: ss };
            if(Object.keys(K2[cc][0]).length > 0)
            {
                tv.categories= K2[cc];
            }
            ca.push(tv);
        });
        return ca;
    }
    var output = [];
    Labels.forEach((ss, i) => {
        if (output.length <= 0) {
            let K1 = groupBy(output, ss);
            Object.keys(K1).forEach(cc => {
                output.push({ name: cc, categories: K1[cc] });
            });
        } else {
            output.forEach(ca => {
                ca.categories = calcCats(ca.categories, ss);
            });
        }
    });
question from:https://stackoverflow.com/questions/65841831/arrange-json-array-to-given-hierarchy-group-without-parent-id

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could take an object which keeps the references to the groups for the actual level, and the array of the results of the level in a nested style and aff a new level from an array of keys.

Let us have a look to the object (_ and the rest properties are switched)

{
    "Male": {
        "21": {
            "_": /**ref:1a**/
        },
        "22": {
            "_": /**ref:6**/
        },
        "23": {
            "_": /**ref:f**/
        },
        "_": /**ref:4**/
    },
    "Female": {
        "21": {
            "_": /**ref:39**/
        },
        "22": {
            "_": /**ref:26**/
        },
        "23": {
            "_": /**ref:31**/
        },
        "_": /**ref:24**/
    },
    "_": [
        {
            "name": "Male",
            "children": [
                /**id:4**/
                {
                    "name": "22",
                    "children": [
                        /**id:6**/
                        { "ID": "A111" },
                        { "ID": "A113" },
                        // ...
                    ]
                },
                {
                    "name": "23",
                    "children": [
                        /**id:f**/
                        { "ID": "A109" },
                        { "ID": "A115" },
                        // ...
                    ]
                },
                {
                    "name": "21",
                    "children": [
                        /**id:1a**/
                        { "ID": "A107" },
                        { "ID": "A101" },
                        // ...
                    ]
                }
            ]
        },
        {
            "name": "Female",
            "children": [
                /**id:24**/
                {
                    "name": "22",
                    "children": [
                        /**id:26**/
                        { "ID": "A114" },
                        { "ID": "A108" },
                        // ...
                    ]
                },
                {
                    "name": "23",
                    "children": [
                        /**id:31**/
                        { "ID": "A112" },
                        { "ID": "A106" },
                        // ...
                    ]
                },
                {
                    "name": "21",
                    "children": [
                        /**id:39**/
                        { "ID": "A104" },
                        { "ID": "A110" },
                        // ...
                    ]
                }
            ]
        }
    ]
}

The object contains nested groups of first level Gender with Male/Female and another level Age with their values and a reference to an array.

This is not the wanted structure for the result.

The result contains arrays of objects with a name property which reflects the level value and later an object without the grouping properties.

In depth:

The outer reduce iterates the given objects. As well as it keeps the object for each iteration and this allowes to use the object with a fast access to the nested groups.

The inner reduce takes a level and a key for this level and destructure the key from the object to get an object without the key for the actual level.

If the level does not have a property with the actual group value, then a new property with the name and an object with only one property _ is created with an array as value. the array contains all values of the level. The underscore is needed to prevent a conflict with other values of the grouping value.

Actually only the first structure is creater, this would not appear in the result set, right now. For having this array in the result set, it need to add an object with a reference to the array as children.

Finall the object with the level group is returned.

At the leaves the rest of the data object has to pushed to the array.

const
    data = [{ Age: "22", Gender: "Male", ID: "A111" }, { Age: "22", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }, { Age: "22", Gender: "Male", ID: "A111" }, { Age: "21", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }, { Age: "22", Gender: "Male", ID: "A111" }, { Age: "21", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }],
    keys = ['Gender', 'Age'],
    tree = data
        .reduce((r, o) => {
            keys
                .reduce((level, key) => {
                    let name;
                    ({ [key]: name, ...o } = o);

                    if (!level[name]) {
                        level[name] = { _: [] };
                        level._.push({ name, children: level[name]._ });
                    }
                    return level[name];
                }, r)
                ._
                .push(o);
            return r;
        }, { _: [] })
        ._;

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...