展平嵌套回调

时间:2014-11-20 22:35:24

标签: javascript arrays node.js callback

这是一个回调(为了这个问题的目的稍作修改/简化)我写的是处理一些对象数据。

  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类型实用程序?别的什么?以上所有?

3 个答案:

答案 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(", "));

遍历算法可能更复杂,具体取决于约束原始数据结构的规则;确保最后保持最小化(即不要编写你不会使用的代码)。