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是一个对象。Caseobj={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"]