在javascript中遍历递归关系矩阵

在javascript中遍历递归关系矩阵,javascript,recursion,matrix,checkbox,Javascript,Recursion,Matrix,Checkbox,有很多元素(用复选框表示)具有一些不同的关系。例如: A需要B A需要C A不能与D结合 B不能与E结合 D需要E C需要F F不能与G结合 S不能与B结合 T不能与D结合 美国需要美国 编辑:答案中出现了3个问题,我想在这里定义这些问题: 问:什么是默认、禁止或需要的?A:没有。如果 两个元素之间没有关系,它们可以独立工作 (只要与一个公共元素没有关系 否则) 问:如果A禁止B,B是否会自动禁止A?A:是的。 猫(A)说,你不能和狗(B)在一起。即使狗 不关心猫,你不能把它们结合起来,因

有很多元素(用复选框表示)具有一些不同的关系。例如:

  • A需要B
  • A需要C
  • A不能与D结合
  • B不能与E结合
  • D需要E
  • C需要F
  • F不能与G结合
  • S不能与B结合
  • T不能与D结合
  • 美国需要美国
编辑:答案中出现了3个问题,我想在这里定义这些问题:

  • 问:什么是默认、禁止或需要的?A:没有。如果 两个元素之间没有关系,它们可以独立工作 (只要与一个公共元素没有关系 否则)

  • 问:如果A禁止B,B是否会自动禁止A?A:是的。 猫(A)说,你不能和狗(B)在一起。即使狗 不关心猫,你不能把它们结合起来,因为猫 我不会喜欢的

  • 问:如果A需要B,B是否自动需要A?不,如果你愿意的话 要读取stackoverflow(A),您需要浏览器(B)。但是如果你想 要使用浏览器(B),您不需要stackoverflow(A)

编辑:我想举一个更简单的例子。比方说,您可以使用复选框配置汽车。有一些规则。例如,如果选择黑色油漆,则不能选择白色内饰颜色(禁止)。如果您选择真皮座椅,则只能与座椅加热(需要)和真皮方向盘(需要)结合使用,但不能与电动座椅调节(禁止)结合使用。白色内饰不允许座椅加热(禁止),而白色车顶需要白色内饰。因此,即使没有定义,也不能使用带有座椅加热的白色车顶(由于与公共元素的关系而被禁止)

因此,如果有人激活复选框A,复选框B和C也需要被激活,而复选框D需要被禁用。因为A需要B,B不能与E组合,所以复选框E也需要禁用。因为C需要F,所以F需要被激活。由于F不能与G结合,G也需要去激活

反过来说:如果有人激活了E,那么B需要被禁用,因为B不能与E结合。但是D不需要被激活,因为D需要E,而E不一定需要D

现在的大问题是:

  • 如何在javascript中理想地表达关系
  • 如果有人激活,如何检查与javascript的所有关系 复选框
  • 问题在于递归。每一个行动都会导致更多的行动,而这些行动(可能)会导致更多的行动

    以下逻辑应适用于激活“A”的示例:

  • B将被激活
  • C将被激活
  • D将被禁用
  • E将被禁用
  • 手机将被禁用
  • T将被禁用
  • 你将被禁用
  • 关系的当前定义(可以更改):

    目前的理论方法:

    假设点击“a”:

    但这只是第一次迭代。从理论上讲,这可能是一个函数,在执行每个操作后递归调用自己。但是,假设300个元素有很多关系,它循环无限。 好吧,如果采取一个操作,激活一个复选框,它就可以正常工作。在一个更现实的场景中,有30%到50%的复选框处于活动状态,关系的检查需要一直上下进行


    第二个问题是:如果用户再次禁用复选框A,则需要再次检查所有关系-对于所有仍处于活动状态的复选框也是如此。

    编辑:要求已细化

    简单的递归不行

    var relations = {
      'A': {
        'B': 'needed',
        'C': 'needed',
        'D': 'prohibited'
      },
      'B': {
        'E': 'prohibited'
      },
      'D': {
        'E': 'needed'
      },
      'E':{/* added for simplicity */},
      'C': {
        'F': 'needed'
      },
      'F': {
        'G': 'prohibited'
      }
    };
    var tmp = {};
    function checkRelations(start) {
      for (var relation in relations[start]) {
        if (!tmp.hasOwnProperty(relation)) {
           tmp[relation] = {};
        }
        if (relations[start][relation] === 'needed') {
          tmp[relation][start] = 'needed';
        } else if (relations[start][relation] === 'prohibited') {
          tmp[relation][start] = 'prohibited';
        }
        checkRelations(relation);
      }
    }
    function run(obj) {
      for (var e in obj) {
        checkRelations(e);
      }
    }
    
    run(relations);
    JSON.stringify(tmp);
    
    将得到以下结果:

    {
      'B': {
        'A': 'needed',
        'S': 'prohibited'
      },
      'E': {
        'B': 'prohibited',
        'D': 'needed'
      },
      'C': {
        'A': 'needed'
      },
      'F': {
        'C': 'needed'
      },
      'G': {
        'F': 'prohibited'
      },
      'D': {
        'A': 'prohibited',
        'T': 'prohibited'
      },
      'S': {
        'U': 'needed'
      }
    }
    
    正如您从第一个条目
    B
    中看到的:您的数据库定义不足。每个未定义的元素会发生什么情况?如果未定义某项内容,默认设置是什么?是
    禁止的还是“需要的”?如果
    'A'
    需要
    'B'
    这是否意味着
    'B'
    需要
    'A'

    一旦定义了,您就可以(自动)填充数据库的第一层,并在此基础上构建一个树(dito自动),如果您需要,可以通过花费大量内存(O(n^2))来确保大量处理(O(n^2))

    所有这些都是在假设整个事物是一致的,并且在任何地方都没有无限循环的情况下发生的

    默认值设置为
    'meh'
    时,第一轮为

    function checkRelations(start) {
      for (var relation in relations[start]) {
        if (!relations.hasOwnProperty(relation)) {
          relations[relation] = {
          };
        }
        if (relations[start][relation] === 'needed') {
          relations[relation][start] = 'meh';
        } else if (relations[start][relation] === 'prohibited') {
          relations[relation][start] = 'prohibited';
        }
        for (var r in relations) {
          if (!relations[relation].hasOwnProperty(r) && relation != r) {
            relations[relation][r] = 'meh';
          }
        }
      }
    }
    function run(obj) {
      for (var e in obj) {
        // fill database up
        checkRelations(e);
      }
    }
    run(relations);
    JSON.stringify(relations)
    {
      'A': {
        'B': 'needed',
        'C': 'needed',
        'D': 'prohibited',
        'E': 'meh',
        'F': 'meh',
        'S': 'meh',
        'T': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'B': {
        'E': 'prohibited',
        'A': 'meh',
        'D': 'meh',
        'C': 'meh',
        'F': 'meh',
        'S': 'prohibited',
        'T': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'E': {
        'B': 'prohibited',
        'A': 'meh',
        'D': 'meh',
        'C': 'meh',
        'F': 'meh',
        'S': 'meh',
        'T': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'D': {
        'E': 'needed',
        'A': 'prohibited',
        'B': 'meh',
        'C': 'meh',
        'F': 'meh',
        'S': 'meh',
        'T': 'prohibited',
        'U': 'meh',
        'G': 'meh'
      },
      'C': {
        'F': 'needed',
        'A': 'meh',
        'B': 'meh',
        'E': 'meh',
        'D': 'meh',
        'S': 'meh',
        'T': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'F': {
        'G': 'prohibited',
        'A': 'meh',
        'B': 'meh',
        'E': 'meh',
        'D': 'meh',
        'C': 'meh',
        'S': 'meh',
        'T': 'meh',
        'U': 'meh'
      },
      'S': {
        'B': 'prohibited',
        'A': 'meh',
        'E': 'meh',
        'D': 'meh',
        'C': 'meh',
        'F': 'meh',
        'T': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'T': {
        'D': 'prohibited',
        'A': 'meh',
        'B': 'meh',
        'E': 'meh',
        'C': 'meh',
        'F': 'meh',
        'S': 'meh',
        'U': 'meh',
        'G': 'meh'
      },
      'U': {
        'S': 'needed',
        'A': 'meh',
        'B': 'meh',
        'E': 'meh',
        'D': 'meh',
        'C': 'meh',
        'F': 'meh',
        'T': 'meh',
        'G': 'meh'
      },
      'G': {
        'F': 'prohibited',
        'A': 'meh',
        'B': 'meh',
        'E': 'meh',
        'D': 'meh',
        'C': 'meh',
        'S': 'meh',
        'T': 'meh',
        'U': 'meh'
      }
    }
    
    那已经很贵了。您可以展开树,但我会停在这里,按照路径
    'needed'
    动态构建树。您应该能够使用第一个递归方法来执行此操作,如果您发现一个“
    需要”
    ”(如果默认值为
    “禁止”

    例如:

    A - B(n) - C(n) - D(p)        
        |       |
        E(p)   F(n)
        ||      |
        S(p)   G(p)
    
    (一根是树枝,两根是叶子)

    当然,根据默认值处理
    'meh'
    部分。如果默认值为
    'panbited'
    ,则您甚至可以完全跳过
    'meh'
    条目的构造,而只剩下
    'needed'
    条目

    剩下的就是

    {
      'A': {
        'B': 'needed',
        'C': 'needed'
      },
      'B': {
        'nothing':0
      },
      'E': {
        'nothing':0
      },
      'D': {
        'E': 'needed'
      },
      'C': {
        'F': 'needed'
      },
      'F': {
        'nothing':0
      },
      'S': {
        'nothing':0
      },
      'T': {
        'nothing':0
      },
      'U': {
        'S': 'needed'
      },
      'G': {
        'nothing':0
      }
    }
    
    并降至最低限度:

    {
      'A': {
        'B': 'needed',
        'C': 'needed'
      }
      'D': {
        'E': 'needed'
      },
      'C': {
        'F': 'needed'
      }
      'U': {
        'S': 'needed'
      }
    }
    
    完整算法:如果需要,将所有条目设置为
    “禁止”
    ,并遍历上面列出的由以下小脚本生成的小数据库

    function checkRelations(start) {
      for (var relation in relations[start]) {
        if (relations[start][relation] === 'prohibited') {
          delete relations[start][relation];
        }
      }
    }
    function isEmpty(obj) {
      for(var p in obj) {
        if(obj.hasOwnProperty(p)){
          return false;
        }
      }
      return true;
    }
    function run(obj) {
      for (var e in obj) {
        // fill database up
        checkRelations(e);
        // delete empty entries
        if(isEmpty(relations[e])){
          delete relations[e];
        }
      }
    }
    
    遍历最后一个的函数:

    function followPath(start){
      for (var relation in reduced[start]) {
        console.log(relation + " is needed")
        if (reduced.hasOwnProperty(relation)) {
          console.log( relation + " is needed, follow path")
          followPath(relation);
        }
      }
    
    }

    啊,太晚了。再次;-)


    但至少比我一开始想象的要简单一些(如果我正确计算循环的话,速度更快)。

    您作为示例给出的关系可以表示为带标记边的有向图:

    在上面的图像中,边的颜色表示边的标签或标记:
    绿色:需要
    红色:禁止

    边上的点代表一个方向
    function checkRelations(start) {
      for (var relation in relations[start]) {
        if (relations[start][relation] === 'prohibited') {
          delete relations[start][relation];
        }
      }
    }
    function isEmpty(obj) {
      for(var p in obj) {
        if(obj.hasOwnProperty(p)){
          return false;
        }
      }
      return true;
    }
    function run(obj) {
      for (var e in obj) {
        // fill database up
        checkRelations(e);
        // delete empty entries
        if(isEmpty(relations[e])){
          delete relations[e];
        }
      }
    }
    
    function followPath(start){
      for (var relation in reduced[start]) {
        console.log(relation + " is needed")
        if (reduced.hasOwnProperty(relation)) {
          console.log( relation + " is needed, follow path")
          followPath(relation);
        }
      }
    
    var graph = { v: [], e: [] } // v: vertices or nodes, e: edges
    
    var vertex = { 
      name: "A",
      el : someElement
    };
    
    var edge = {
      s: sourceVertex,
      d: destinationVertex,
      tag: "n"   // the tag will define the type of relation
      // again you can use strings: 'n' - `needs` and 'p' - `prohibits`
      // or whatever type you like       
    };