使用对象迭代JSON

时间:2016-05-11 14:35:16

标签: javascript arrays json

我正在创建模板选择器/轮盘Slack机器人。目标是向机器人发送消息并传递命令行之类的参数,并让它遍历JSON对象并随机选择符合条件的模板。用户也应该能够传递任何数量的参数。

示例:我使用此Roulette -s -i向机器人发送消息-s代表侧栏,-i代表索引。因此,机器人需要找到一个既有侧边栏又有索引的模板。

这是我的JSON对象的示例:

{
  name: "foo",
  index: {
    hasIndex: true
  },
  sidebar: {
    hasSidebar: true
  }
}

这是我的代码:

controller.hears(["roulette", "^pattern$"],["direct_message", "direct_mention", "mention"], function(bot,message) {
  const data = require('./data.js');
  const nodeList = []
  const args = message.text.match(/-\w/g);
  const obj = {
    '-s': '.sidebar.hasSidebar',
    '-i': '.index.hasIndex'
  }

  args.forEach(function(arg) {
    var path = obj[arg];
    // console.log(path) when passing arguments -s or -i
    // the path variable correctly gives me .sidebar.hasSidebar, etc    

    data.forEach(function(node) {
      // console.log(node) Gives me my data set
      // console.log(node.path) This is the issue, it returns undefined.
      if (node.path === true) {
        nodeList.push(node.name)
      }
    });
  });
});

我的问题是,如果node.path正确地向我提供数据集,为什么我不能使用node?难道我不能添加path并完成路径以便forEach循环通过吗?相反,我得到了undefined,我不明白为什么。

修改

我已更新了我的代码:

  var nodeList = data.filter(function(node) {
    return args.every(function(arg) {
      return obj[arg](node);
    });
  });

  const obj = {
    '-s': function (node) { return node.sidebar.hasSidebar; },
    '-i': function (node) { return node.index.hasIndex; },
  };


  data.forEach(function(node) {
    args.forEach(function(arg) {
      var pathTester = obj[arg];

      if (pathTester(node)) {
        nodeList.push(node.name)
      }
    });
  });

1)我是否正确交换了循环?

2)我不太了解nodeList应该如何工作。

1 个答案:

答案 0 :(得分:0)

当你写:

if (node.path === true)

...您正在寻找名为path的媒体资源。这与您之前声明的变量path无关。

使用此语法无法基于点分隔的字符串(如'.sidebar.hasSidebar')查找动态属性链。您必须致电eval()才能实现这一目标,如下所示:

if (eval('node' + path) === true)

这样做有效,但由于eval名声不好,我建议您使用函数而不是字符串来定义obj,如下所示:

const obj = {
    '-s': function (node) { return node.sidebar.hasSidebar; },
    '-i': function (node) { return node.index.hasIndex; },
};

...然后检索并调用函数:

args.forEach(function(arg) {
    var pathTester = obj[arg];

    data.forEach(function(node) {
        if (pathTester(node)) {
            nodeList.push(node.name);
        }
    });
});

算法修正

现在,上面回答了这个问题,但正如你提到的那样:

  

示例:我使用此Roulette -s -i向机器人发送消息-s代表侧栏,-i代表索引。因此,机器人需要找到一个既有侧边栏又有索引的模板。

...我需要指出,即使只有一个条件为真,您当前的代码也会将节点推送到节点列表,并且可能会多次推送相同的节点。

如果您希望仅在所有条件都为真时添加节点,那么交换您的循环并确保只有当(然后)内部循环为所有参数返回true时才添加节点。

同时我建议使用everyfilter函数:

var nodeList = data.filter(function(node) {
    return args.every(function(arg) {
        return obj[arg](node);
    });
});

请注意,此会替换您拥有的forEach循环。完整的代码如下所示:

controller.hears(["roulette", "^pattern$"],["direct_message", "direct_mention", "mention"], function(bot,message) {
    const data = require('./data.js');
    const args = message.text.match(/-\w/g);
    const obj = {
        '-s': function (node) { return node.sidebar.hasSidebar; },
        '-i': function (node) { return node.index.hasIndex; },
    };
    var nodeList = data.filter(function(node) {
        return args.every(function(arg) {
            return obj[arg](node);
        });
    });
});

有关filterevery

的更多信息

以上是filterevery的强大功能。如果没有这些方法,代码可能如下所示:

var nodeList = [];
data.forEach(function(node) {
    // Check if this node should be added
    var addNode = true; // start positive...
    args.forEach(function(arg) {
        var pathTester = obj[arg];
        if (pathTester(node) !== true) {
            // if not all paths are true, don't add the node 
            addNode = false;
        }
    });
    if (addNode) {
        nodeList.push(node);
    }
});

有关与true

进行比较的更多信息

编写这样的代码时:

if (property === true)

... 你肯定知道属性总是一个布尔值(如果已定义),那么你可以这样写:

if (property)

这是因为if语句需要一些评估为布尔值的语句。但如果 property 已经是布尔值,则无需与true进行比较。 属性有三种可能性。它是:

  • undefined
  • false
  • true

当这三个值中的一个是if表达式时,只有最后一个true将成为if条件trueundefined被视为虚假价值。

但是,如果属性也可以是非布尔值,如数字,字符串或对象,并且您只想响应true的确切值(不是任何值)其他值),那么需要像=== true那样进行测试。没有它,非零数值,非空字符串和对象将通过测试。