使用对象通过JSON进行迭代

Iterating through JSON using an object

本文关键字:迭代 JSON 对象      更新时间:2023-09-26

我正在创建一个模板选择器/轮盘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正确地为我提供了数据集,为什么我不能使用node.path?难道我不应该添加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应该如何工作。

编写时:

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时添加节点,则交换循环,并确保仅在(然后)内部循环对所有参数返回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语句需要计算为布尔值的东西。但是,如果属性已经是布尔值,则无需与true进行比较。属性有三种可能性。要么是:

  • undefined
  • false
  • true

当这三个值中的一个是if表达式时,只有最后一个true会使if条件成为true。CCD_ 34被认为是一个伪值。

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