Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/404.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 将具有n个级别的嵌套对象转换为数组_Javascript_Jquery - Fatal编程技术网

Javascript 将具有n个级别的嵌套对象转换为数组

Javascript 将具有n个级别的嵌套对象转换为数组,javascript,jquery,Javascript,Jquery,我有一个嵌套对象,类似于: var obj = { "prop1": { "prop1A": "A", "prop1B": { "prop1BA": "BA" }, "prop1C": "C" } }; 我的最终目标是根据另一个模式对象,将此对象筛选为特定的预定义键,例如: var filterSchema = { "prop1":["prop1A", {"prop1B":

我有一个嵌套对象,类似于:

var obj = {
    "prop1": {
       "prop1A": "A",
       "prop1B": {
                  "prop1BA": "BA"
       }, 
       "prop1C": "C"
    }  
};
我的最终目标是根据另一个模式对象,将此对象筛选为特定的预定义键,例如:

var filterSchema = {
    "prop1":["prop1A", {"prop1B":["prop1BA"]}]
};
function mkFilter(schema) {
    var parts = [];
    function xtract(s, prefix) {
        if (typeof s === "string") {
            parts.push(prefix + s);
        } else if (s && s.constructor === Array) {
            s.forEach(function(x){ xtract(x, prefix); });
        } else {
            for (var f in s) {
                xtract(s[f], prefix + f + ".");
            }
        }
    }
    xtract(schema, "obj.");
    var code = "(function(obj){ return [" + parts.join(", ") + "]; })";
    return eval(code);
}
(过滤键是预定义的,如果您有更好的想法,我可以以不同的方式构造此对象

输出应为数组。在本例中:

["A","BA"]
我在对象上使用递归实现了这一点。我想知道是否有更优雅的方法来实现这一点(尝试使用jQuery的map/extend,但没有成功)

编辑 我知道这是一个“N”级的问题,应该通过递归来解决。这里的区别是我有一个预定义的过滤器,它已经有了“N”级。所以我想也许我可以使用过滤器数组过滤对象,然后将其转换成数组

EDIT2 谢谢大家给出了不同的答案。这是我自己对这个问题的解决方案(一开始我一直在寻找一个更优雅的解决方案):

var obj={
“建议1”:{
“prop1A”:“A”,
//“prop1B”:{
//“prop1BA”:“BA”
//}, 
“prop1C”:“C”,
“prop1D”:“D”,
“prop1E”:{“prop1E1”:“444”}
},
“prop2”:“12345”
};
var schemaObj={
“建议1”:{
“prop1A”:“正确”,
“prop1B”:{
“prop1BA”:“正确”
}, 
“prop1C”:“正确”
},
“建议2”:“正确”
};
var结果数组=[];
var keys=Object.keys(schemaObj);

for(var i=0;iJavascript具有
eval
,允许您在运行时创建新代码。一种可能的解决方案是只使用一次递归来创建如下字符串:

code = "[obj.prop1.prop1A, obj.prop1.prop1B.prop1BA]"
然后,您可以使用创建数据转换函数

f = eval("function(obj){return " + code + "]}")
并将其与
f(x)
一起使用以获取阵列

如果必须多次提取数据,这也是最有效的解决方案

例如:

var filterSchema = {
    "prop1":["prop1A", {"prop1B":["prop1BA"]}]
};
function mkFilter(schema) {
    var parts = [];
    function xtract(s, prefix) {
        if (typeof s === "string") {
            parts.push(prefix + s);
        } else if (s && s.constructor === Array) {
            s.forEach(function(x){ xtract(x, prefix); });
        } else {
            for (var f in s) {
                xtract(s[f], prefix + f + ".");
            }
        }
    }
    xtract(schema, "obj.");
    var code = "(function(obj){ return [" + parts.join(", ") + "]; })";
    return eval(code);
}
schemaFilter
作为参数
mkFilter
传递将返回一个函数,该函数给定一个对象,将返回数组;使用您的输入:

console.log(mkFilter(filterSchema)(obj));
显示
['A','BA']
。当然,如果需要对不同的对象多次重复使用同一筛选器,这种方法是有意义的

如果对象可能缺少部分,并且您不希望过滤器失败,而只是阵列中的
未定义的
值,则需要稍微更改代码生成器:

var code = "(function(obj){ return [";
parts.forEach(function(p){
    var chain = p.split(".");
    var expr = "";
    for (var i=0; i<chain.length-1; i++) {
        expr += chain.slice(0, i+1).join(".") + " && ";
    }
    code += expr + p + ",";
});
code += "]; })";

以下函数似乎可以实现您想要的功能:

function filter(obj, schema, out) {
    var i, schemaItems, schemaItem, isItemLevel;

    if (!obj || !schema) return;

    out = out || {values: []};
    isItemLevel = Array.isArray(schema);
    schemaItems = isItemLevel ? schema : Object.keys(schema);

    for (i = 0; i < schemaItems.length; i++) {
        schemaItem = schemaItems[i];
        if (isItemLevel && typeof schemaItem === "string") {
            out.values.push(obj[schemaItem]);
        } else if (typeof schemaItem === "object") {
            filter(obj, schemaItem, out);
        } else if (typeof schemaItem === "string") {
            filter(obj[schemaItem], schema[schemaItem], out);
        }
    }

    return out.values;
}
返回:

["A", "BA"]
恕我直言,到目前为止,它还没有得到足够的测试,我当然不认为这是解决这个问题的最优雅的方法

它的工作原理如下:

  • 遍历
    架构
    中的项目(它是数组或对象)
  • 对于每个
    schemaItem
    • 如果我们在一个数组中,并且
      schemaItem
      是一个字符串,则输出
      obj
    • 否则,如果
      schemaItem
      本身是一个对象,则递归,但在
      obj
    • 否则,如果
      schemaItem
      是字符串,则递归,钻取
      obj
      schema

使用jquery的映射功能。您可以在控制台中尝试示例代码段,但必须包含jquery

a = { aa: '123', ab: 'asdasd'}
$.map(a, function(key,val){
    return val;
});
// The map creates an array with the value you return from the code block.
// Output is ["aa", "ab"]
有关参考信息,请参见

删除了对JSON的所有引用。奇怪的是,递归在这里怎么不雅观呢?似乎(撇开性能缩放问题)是一个理想的用例…这里没有别的方法,只有递归比什么更雅观?您还没有向我们展示您的解决方案。我相信您的示例所需的输出应该是
[“A”,“BA”]
,否?除此之外,您的两个示例在语法上都无效。我感觉您自己想要的东西还没有一个准确、可靠的规范……只有当所有请求的属性都在源对象中时,它才起作用。(仅供参考,不是我的否决票)@Tomalak:您可以将调用封装在
try
中,或者生成一个类似
[obj&&obj.prop1&&obj.prop1.prop1A,…]
的表达式,但这可能会在结果数组中留下很多“未定义”的值。但这仍然是一个创造性的解决方案。@Tomalak:可能有些jquery偏执者甚至不理解这个术语“元编程”。没有明确的规范很难说,但你的代码似乎总是假设filterSchema是一个对象。Case
obj={x:“a”}
filterSchema=[“x”]
无效?我想结果应该是
[“a”]
。是的,我想是这样的(不得不猜测很烦人)。我想嵌套循环可以取消嵌套以支持这种情况。具有两个递归点的函数无论如何都感觉不正确。在我看来,两个递归调用似乎是最干净的方法,即使嵌套数组对于这种
filterSchema
格式来说毫无意义。@6502我已经解开了嵌套循环。正如你所说,仍然有两个递归ion points是必需的,但总体上代码更少&更干净,再加上您建议的情况都有效。这是一种基于运行时的好方法。不过,我认为仅仅省略未找到的项而不是添加
未定义的
或引发错误是一个坏主意。使用问题中的模式调用函数,只得到
['Y']
在我看来,没有人告诉我找到了哪一个元素是无用的。有几件事。你的回调参数是反向的,我认为这不符合OP的需要。你是对的@squint我不知道数组原型中的map方法,谢谢
a = { aa: '123', ab: 'asdasd'}
$.map(a, function(key,val){
    return val;
});
// The map creates an array with the value you return from the code block.
// Output is ["aa", "ab"]