这是一个回调(为了这个问题的目的稍作修改/简化)我写的是处理一些对象数据。
function (err, data) {
var rv;
if (data && data.result instanceof Array) {
rv = data.result.map(function (value) {
if (value && value.track_contributions instanceof Array) {
return value.track_contributions.map(function (innerValue) {
if (innerValue && innerValue.track) {
return innerValue.track.mid;
}
});
}
});
// flatten nested arrays
rv = [].concat.apply([], rv);
}
console.log(rv);
};
以下是我正在处理的对象:
{
"result": [
{
"type": "/music/artist",
"track_contributions": [
{
"track": {
"mid": "/m/015rm3l"
}
},
{
"track": {
"mid": "/m/0nm2km"
}
},
{
"track": {
"mid": "/m/010ksbq"
}
},
...
]
}
]
}
我希望回调值看起来像这样:
[ '/m/015rm3l',
'/m/0nm2km',
'/m/010ksbq',
...
]
我的代码工作正常,但我觉得所有嵌套都是代码味道。
我应该如何使这种类型的代码更平坦,更易读和可维护,以及所有这些伟大的东西?承诺?一些lodash类型实用程序?别的什么?以上所有?
答案 0 :(得分:1)
这是一个我认为更容易理解和维护的版本,没有重复的代码,因为它使用带回调的通用遍历来添加数据结构的细节:
function process(err, data) {
var rv = [];
function doObject(obj, key, callback) {
if (obj[key] && obj[key] instanceof Array) {
obj[key].forEach(function(value) {
callback(value);
});
}
}
doObject(data, "result", function(result) {
doObject(result, "track_contributions", function(item) {
if (item.track && item.track.mid) {
rv.push(item.track.mid);
}
});
});
console.log(rv);
}
在不更改您正在使用的基本算法的情况下,您可以初始化rv
并将结果直接推送到rv
,而不是制作必须展平的子数组。
function process(err, data) {
var rv = [];
if (data && data.result instanceof Array) {
data.result.forEach(function(value) {
if (value && value.track_contributions instanceof Array) {
value.track_contributions.forEach(function(innerValue) {
if (innerValue && innerValue.track) {
rv.push(innerValue.track.mid);
}
});
}
});
}
console.log(rv);
}
因为您在每个级别寻找的属性名称不同,所以使用递归来避免重复代码(尽管这是一个选项)并不是那么高效或容易。
这是一个两级递归算法,但我不认为增加的复杂性实际上是值得的。如果你的深度超过2级或任意数量的级别,它可能更有意义:
function process(err, data) {
var rv = [];
function doArray(item, key1, key2) {
if (item && item[key1] instanceofArray) {
item[key1].forEach(function(value) {
if (key2) {
doArray(value, key2);
} else if (value && value.track.mid) {
rv.push(value.track.mid);
}
});
}
}
doArray(data, "result", "track_contributions");
console.log(rv);
}
答案 1 :(得分:1)
这种方法可以获得所需的输出:
function getArray(err, data) {
var rv;
function mapArray(obj, array, callback) {
if (obj && obj[array] instanceof Array) {
return obj[array].map(callback);
}
}
function getValue(value) {
return mapArray(value, 'track_contributions', getInnerValue);
}
function getInnerValue(innerValue) {
return innerValue.track && innerValue.track.mid;
}
rv = [].concat.apply([], mapArray(data, 'result', getValue));
console.log(rv);
};
getArray(null, data);
关键是将每个函数分成所需的目的,并重用每个重复的函数。
例如,映射完成两次。因此,我们为此编写一个通用函数mapArray
。它需要obj
,数组属性array
的名称和回调。
对于示例中的外部函数,我们有mapArray(data, 'result', getValue)
,而内部函数是mapArray(value, 'track_contributions', getInnerValue)
。
在getValue
函数内部,我们定义了外部函数,getInnerValues
定义了内部函数。
完成。
答案 2 :(得分:0)
您将数据结构的遍历与要应用于其节点的域逻辑混合在一起,因此代码气味。最好将这两个方面解耦,使用域函数提供的通用遍历算法(在当前情况下是收集器)。
例如:
function traverse(source, collector) {
for(var property in source) {
var value = source[property];
if (value instanceof Array) {
for(var ii=0; ii<value.length; ii++) {
arguments.callee(value[ii], collector);
}
} else if (value instanceof Object){
arguments.callee(value, collector);
} else {
collector(property, value);
}
}
}
var values=[];
traverse(source, function(property, value) {
if (property == "mid") {
values.push(value);
}
});
document.write(values.join(", "));
遍历算法可能更复杂,具体取决于约束原始数据结构的规则;确保最后保持最小化(即不要编写你不会使用的代码)。