列出所有嵌套属性及其在包含对象中的完整路径

List all nested properties with their full path in the containing object

我有一些名称数组存储在嵌套的 JSON 中,如下所示:

{
    "groupZ": {
        "names": [
            "Steve",
            "Henry"
        ]
    },
    "groupY": {
        "groupA": {
            "names": [
                "Laura"
            ]
        },
        "groupB": {
            "names": [
                "Alice",
                "Bob",
                "Neil"
            ]
        }
    },
    "groupX": {
        "groupC": {
            "groupD": {
                "names": [
                    "Steph"
                ]
            }
        },
        "groupE": {
            "names": [
                "Aaron",
                "Dave"
            ]
        }
    }
}

我想弄清楚如何生成所有名称的列表,并为每个名称添加完整的组路径,所以它最终是这样的:

每个级别的组名都是唯一的,但除此之外可以任意命名。我知道我需要递归调用一个函数,该函数在找到 "names" 数组时停止,传递一个字符串以在每次递归时添加到前缀中,但遇到了真正的麻烦。到目前为止,这是我的代码:

var sPrepend = '';
function buildList(Groups, lastGroupName){

    for(var thisGroupName in Groups) {
        var thisGroup = Groups[thisGroupName];

        if(!thisGroup.names){
            sPrepend += (' - ' + thisGroupName);
            buildList(thisGroup, thisGroupName);
        }
        if(thisGroup.names){
            thisGroup.names.forEach(function(name){
                console.log(sPrepend, ' - ', name);
                //build the list item here.
            });
        }
    }
}
buildList(oGroups, '');

这让我很困惑,因为我无法更改 JSON 结构,但我确信这是可能的。感谢任何可以提供帮助的人!

这对我有用:

function buildList(group, accum, key) {

    // default helper parameters
    accum = accum || [];
    key = key || '';

    if (group.names) {  // add each name
        group.names.forEach(function(name) {
            accum.push(key + name);
        });
    } else {            // or recurse on each key
        Object.getOwnPropertyNames(group).forEach(function(gname) {
            buildList(group[gname], accum, key + gname + ' - ');
        });
    }

    return accum;
}

var list = buildList(oGroups);

是的,你很接近。您只需要确保将连接的组名称(以前的 + 当前的)传递给递归函数。

Alnitak 的解决方案总体上更简洁。

function buildList(Groups, lastGroupName){

    for(var thisGroupName in Groups) {
        var thisGroup = Groups[thisGroupName];
        
        var currGroupName = lastGroupName + ' - ' + thisGroupName;

        if(!thisGroup.names){
            buildList(thisGroup, currGroupName);
        }
        if(thisGroup.names){
            thisGroup.names.forEach(function(name){
                console.log(currGroupName + ' - ' + name);
                //build the list item here.
            });
        }
    }
}

var oGroups = {
    "groupZ": {
        "names": [
            "Steve",
            "Henry"
        ]
    },
    "groupY": {
        "groupA": {
            "names": [
                "Laura"
            ]
        },
        "groupB": {
            "names": [
                "Alice",
                "Bob",
                "Neil"
            ]
        }
    },
    "groupX": {
        "groupC": {
            "groupD": {
                "names": [
                    "Steph"
                ]
            }
        },
        "groupE": {
            "names": [
                "Aaron",
                "Dave"
            ]
        }
    }
};

buildList(oGroups, '');

我想你也想 return 这最终作为一个列表,这需要更多的努力。

function buildList(Groups, lastGroupName){

    var ret = [];

    for(var thisGroupName in Groups) {
        var thisGroup = Groups[thisGroupName];
        
        var currGroupName = lastGroupName + ' - ' + thisGroupName;

        if(!thisGroup.names){
            var childNames = buildList(thisGroup, currGroupName);
            for(var i = 0; i < childNames.length; i++) {
                ret.push(childNames[i]);
            }
        }
        if(thisGroup.names){
            thisGroup.names.forEach(function(name){
                //console.log(currGroupName + ' - ' + name);
                //build the list item here.
                ret.push(currGroupName + ' - ' + name);
            });
        }
    }

    return ret;
}

var oGroups = {
    "groupZ": {
        "names": [
            "Steve",
            "Henry"
        ]
    },
    "groupY": {
        "groupA": {
            "names": [
                "Laura"
            ]
        },
        "groupB": {
            "names": [
                "Alice",
                "Bob",
                "Neil"
            ]
        }
    },
    "groupX": {
        "groupC": {
            "groupD": {
                "names": [
                    "Steph"
                ]
            }
        },
        "groupE": {
            "names": [
                "Aaron",
                "Dave"
            ]
        }
    }
};

var x = buildList(oGroups, '');
for(var i = 0; i < x.length; i++) {
    console.log(x[i]);
}

您可以采用一个数组作为路径并调用不带该参数的函数,因为它会被检查,如果没有truthy,则会分配一个数组。

在对象的键循环中,检查键是否为names,并显示此数组和路径。

如果键不等于names,则检查实际的属性,如果它是一个可迭代对象,则使用带有实际键的扩展数组再次调用该函数.

function buildList(object, path) {
    path = path || [];
    Object.keys(object).forEach(function (k) {
        if (k === 'names') {
            object.names.forEach(function (name) {
                console.log(path.concat(name).join(' - '));
            });
            return;
        }
        if (object[k] && typeof object[k] === 'object') {
            buildList(object[k], path.concat(k));
        }
    });
}

var data = { groupZ: { names: ["Steve", "Henry"] }, groupY: { groupA: { names: ["Laura"] }, groupB: { names: ["Alice", "Bob", "Neil"] } }, groupX: { groupC: { groupD: { names: ["Steph"] } }, groupE: { names: ["Aaron", "Dave"] } } };

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