【问题标题】:Create string out of multi dimensional children array with group nodes使用组节点从多维子数组创建字符串
【发布时间】:2021-02-02 04:32:08
【问题描述】:

我有这个深度嵌套的数组,其中包含一组节点,我想创建类似的查询字符串

(FULL_NAME="x" AND NOT(AGE="30" OR AGE="40" AND (ADDRESS="y" AND STREET="z" AND NOT(USER="admin" OR USER="super admin"))) AND TITLE="Developer")

我拥有的json数据是,

  • 接收用户输入的节点列表
  • 处理多节点加入的算子
  • 如果用户选择NOT,包含它的主容器将没有节点,所以它看起来像这样NOT (X="y" AND Y="x")
  • 第一个垂直运算符是子节点的连接器,如果我们选择使用 NOT 运算符,则此容器将成为一个组,并且其中将包含另一个容器,其中包含 ANDOR
  • 用户可以在NOTANDOR 之间进行选择,但如果他们尝试对它们进行分组,则不能使用相同的运算符,这仅适用于NOT 运算符。

我的数组看起来像:

[
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
]

在 UI 中看起来像这样

我已经尝试了几种方法来从中生成字符串,但我无法对深度嵌套的节点进行分组。

谢谢。

【问题讨论】:

  • 请从给定的数据中添加想要的字符串。 'BETWEEN' 会发生什么?
  • @NinaScholz 我们有这个BETWEEN "x" AND "y" 字符串要在操作员之间打印。

标签: javascript json angular typescript multidimensional-array


【解决方案1】:

您可以为节点和子节点使用不同的回调并收集项目以获取字符串。

const
    data = [{ uuid: "b7f0ddf4-0290-4c7e-bb59-771aa46bc850", operator: "AND", isMain: true, nodes: [{ values: { fieldValue: { FieldName: "ORIGINAL_FILE_NAME" }, operator: "=", primaryOperandValue: "new" }, uuid: "779fb920-eb7f-4441-9b5a-886c7a41e271" }], children: [{ uuid: "7467b8c9-212e-41b8-ac02-04296b95c88c", operator: "NOT", nodes: [], children: [{ operator: "AND", uuid: "eaad7c96-0e8f-466b-a255-1075a8e68647", nodes: [{ uuid: "f6057d1b-56d7-4ee6-ac5b-332fbd180fd4", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: "BETWEEN", primaryOperandValue: "x", secondaryOperandValue: "y" } }], children: [{ uuid: "95fd2b08-cc49-498a-bd9f-c50dc55bc39f", operator: "NOT", nodes: [], children: [{ uuid: "7637ecc1-28b4-47d7-a602-cd172fb5e269", operator: "OR", nodes: [{ uuid: "0598a915-5818-4c6e-a3d5-6724f893871a", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: " > ", primaryOperandValue: "30", secondaryOperandValue: null } }], children: [] }] }] }] }, { uuid: "78218b5b-b18b-4418-beed-b3418361785f", operator: "OR", nodes: [{ uuid: "ec956407-4fc6-46df-baa7-d2233711dc20", values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "ENDS_WITH", primaryOperandValue: "log", secondaryOperandValue: null } }, { values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "BEGINS_WITH", primaryOperandValue: "log", secondaryOperandValue: null }, uuid: "6724e913-6e98-47b6-b6af-972a20f0173d" }], children: [] }] }],
    QUOTE = '"',
    wrap = string => `(${string})`,
    quote = string => `${QUOTE}${string}${QUOTE}`,
    isUnary = s => ['NOT'].includes(s),
    getNodes = ({ values: { fieldValue: { FieldName }, operator, primaryOperandValue, secondaryOperandValue } }) => secondaryOperandValue === null || secondaryOperandValue === undefined
        ? `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)}`
        : `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)} AND ${quote(secondaryOperandValue)}`,
    getChildren = ({ operator, nodes = [], children = [] }) => {
        const values = [...nodes.map(getNodes), ...children.map(getChildren)]
        return isUnary(operator)
            ? `${operator} ${values.join('')}`
            : wrap(values.join(` ${operator} `));
    },
    result = data.map(getChildren).join('');

console.log(result);

【讨论】:

    【解决方案2】:

    尝试使用递归,例如

    const queryObject = [
       {
          "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
          "operator":"AND",
          "isMain":true,
          "nodes":[
             {
                "values":{
                   "fieldValue":{
                      "FieldName":"ORIGINAL_FILE_NAME",
                   },
                   "operator":"=",
                   "primaryOperandValue":"new"
                },
                "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
             }
          ],
          "children":[
             {
                "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
                "operator":"NOT",
                "nodes":[],
                "children":[
                   {
                      "operator":"AND",
                      "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                      "nodes":[
                         {
                            "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                            "values":{
                               "fieldValue":{
                                  "FieldName":"CONTROL_NUMBER",
                               },
                               "operator":"BETWEEN",
                               "primaryOperandValue":"x",
                               "secondaryOperandValue":"y"
                            }
                         }
                      ],
                      "children":[
                         {
                            "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                            "operator":"NOT",
                            "nodes":[],
                            "children":[
                               {
                                  "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                                  "operator":"OR",
                                  "nodes":[
                                     {
                                        "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                        "values":{
                                           "fieldValue":{
                                              "FieldName":"CONTROL_NUMBER",
                                           },
                                           "operator":" > ",
                                           "primaryOperandValue":"30",
                                           "secondaryOperandValue":null
                                        }
                                     }
                                  ],
                                  "children":[]
                               }
                            ]
                         }
                      ]
                   }
                ]
             },
             {
                "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
                "operator":"OR",
                "nodes":[
                   {
                      "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                      "values":{
                         "fieldValue":{
                            "FieldName":"EMAIL_ANY_ADDRESS",
                         },
                         "operator":"ENDS_WITH",
                         "primaryOperandValue":"log",
                         "secondaryOperandValue":null
                      }
                   },
                   {
                      "values":{
                         "fieldValue":{
                            "FieldName":"EMAIL_ANY_ADDRESS",
                         },
                         "operator":"BEGINS_WITH",
                         "primaryOperandValue":"log",
                         "secondaryOperandValue":null
                      },
                      "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
                   }
                ],
                "children":[
                   
                ]
             }
          ]
       }
    ]
    
    const operatorString = (items) => items.map(
      ({operator, nodes, children, isMain }) => {
        
        const nodeString = nodes.map(({ values }) => {
          const {fieldValue, operator, primaryOperandValue, secondaryOperandValue} = values
          return fieldValue.FieldName + ' ' + operator + (secondaryOperandValue ?  '("' + primaryOperandValue + '", "' + secondaryOperandValue + '")' : ' "' + primaryOperandValue + '"')
        }).join(" " + operator + " ")
        
        return "( " + nodeString + ' ' + operator + operatorString(children) + " )"
    
      }
    ).join("")
    console.log(operatorString(queryObject))

    【讨论】:

    • 有些运算符太多了,比如最后一个OR ...
    【解决方案3】:

    不完整,但希望这能给你一个好的、可读的开始

    function processNodes( node ) {
      var nodeStr = " Node [" + node.values.operator + "]";
      
      switch (node.values.operator) {
      
        case "=": 
        case " > ":
        case "<":
        case "ENDS_WITH":
        case "BEGINS_WITH":
          nodeStr = node.values.fieldValue.FieldName;
          nodeStr += " " + node.values.operator;
          nodeStr += " " + node.values.primaryOperandValue;
          break;
      }
      return nodeStr;
    }
    
    function processCommands ( cmdArray, operator ) {
      var cmdStr = '';
    
      for (var i = 0; i < cmdArray.length; i++)
      {
      
        if (cmdArray[i].nodes.length > 0) {  
        cmdStr += " ( ";
          for (var j = 0; j < cmdArray[i].nodes.length; j++) {
            if (j > 0) {
              cmdStr += " " + cmdArray[i].operator + " ";
            }
            cmdStr += processNodes(cmdArray[i].nodes[j]);
            
          }
          cmdStr += " ) ";
        }
        if ( cmdStr || cmdArray[i].children.length > 0) {
            cmdStr += " " + cmdArray[i].operator + " ";
        }
          
        if (cmdArray[i].children.length > 0) {      
          cmdStr += " " + processCommands(cmdArray[i].children, cmdArray[i].operator);
        }
      }
    
      return cmdStr;
    
    }
    
    
    var cmdArray = [
        {
           "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
           "operator":"AND",
           "isMain":true,
           "nodes":[
              {
                 "values":{
                    "fieldValue":{
                       "FieldName":"ORIGINAL_FILE_NAME",
                    },
                    "operator":"=",
                    "primaryOperandValue":"new"
                 },
                 "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
              }
           ],
           "children":[
              {
                 "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
                 "operator":"NOT",
                 "nodes":[],
                 "children":[
                    {
                       "operator":"AND",
                       "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                       "nodes":[
                          {
                             "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                             "values":{
                                "fieldValue":{
                                   "FieldName":"CONTROL_NUMBER",
                                },
                                "operator":"BETWEEN",
                                "primaryOperandValue":"x",
                                "secondaryOperandValue":"y"
                             }
                          }
                       ],
                       "children":[
                          {
                             "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                             "operator":"NOT",
                             "nodes":[],
                             "children":[
                                {
                                   "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                                   "operator":"OR",
                                   "nodes":[
                                      {
                                         "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                         "values":{
                                            "fieldValue":{
                                               "FieldName":"CONTROL_NUMBER",
                                            },
                                            "operator":" > ",
                                            "primaryOperandValue":"30",
                                            "secondaryOperandValue":null
                                         }
                                      }
                                   ],
                                   "children":[]
                                }
                             ]
                          }
                       ]
                    }
                 ]
              },
              {
                 "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
                 "operator":"OR",
                 "nodes":[
                    {
                       "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                       "values":{
                          "fieldValue":{
                             "FieldName":"EMAIL_ANY_ADDRESS",
                          },
                          "operator":"ENDS_WITH",
                          "primaryOperandValue":"log",
                          "secondaryOperandValue":null
                       }
                    },
                    {
                       "values":{
                          "fieldValue":{
                             "FieldName":"EMAIL_ANY_ADDRESS",
                          },
                          "operator":"BEGINS_WITH",
                          "primaryOperandValue":"log",
                          "secondaryOperandValue":null
                       },
                       "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
                    }
                 ],
                 "children":[
                    
                 ]
              }
           ]
        }
     ];
    
    console.log(processCommands(cmdArray));
     

    【讨论】:

      【解决方案4】:

      你可以做这样的事情。它使用了一些辅助函数和一个主要的递归函数

      const init = () => {
          
          //  special logic to handle javascript to SQL
          const nodeToClause = (node) => {
              let r = [];
              switch (node.values.operator) {
                  case 'BETWEEN':
                     r = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue,node.values.secondaryOperandValue];
                      break;
                  case '=':
                  case 'BEGINS_WITH':
                  case 'ENDS_WITH':
                      r = [node.values.fieldValue.FieldName,node.values.operator,`"${node.values.primaryOperandValue}"`];
                      break;
                  default:
                     r = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue];
                      break;
              }
              return r.join(' ');
          };
          //  serialize
          const toSQL = (statement) => {
              let r = '';
              const glue = ' ' + statement.operator + ' ';
              if (statement.nodes.length) {
                  r = statement.nodes.map(nodeToClause).join(glue);    
              }
              return r;
          };
          // helps with formatting. not necessary, but nice
          const tabs = (n) => {
              const t = ['\n'];
              let i = 0;
              while (i < n) {
                  t.push('\t');
                  i++;
              }
              return t.join('');
          };
          //  the recursive function that does all the work
          const recurse = (queryFragment, joiner, level=0) => {
              let r = "";
              if (Array.isArray(queryFragment)) {
                  r = queryFragment.map(frag => {
                      return recurse(frag, frag.operator, level);
                  }).join(joiner);
              } else {
                  r = tabs(level) + toSQL(queryFragment);
                  if ("children" in queryFragment && queryFragment.children.length) {
                      r += ' ' + queryFragment.operator + ' (' +  recurse(queryFragment.children,queryFragment.operator,level+1) + tabs(level) + ') ';
                  }
              }
              return r;
          }; 
          
          //  load the data into DOM
          const x = recurse(query,';');
          output.innerText = x;
      
      };
      
      // output to screen
      window.setTimeout(init, 997);
      const output = document.getElementById('output');
      
      // test data
      const query = [
         {
            "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
            "operator":"AND",
            "isMain":true,
            "nodes":[
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"ORIGINAL_FILE_NAME",
                     },
                     "operator":"=",
                     "primaryOperandValue":"new"
                  },
                  "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
               }
            ],
            "children":[
               {
                  "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
                  "operator":"NOT",
                  "nodes":[],
                  "children":[
                     {
                        "operator":"AND",
                        "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                        "nodes":[
                           {
                              "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                              "values":{
                                 "fieldValue":{
                                    "FieldName":"CONTROL_NUMBER",
                                 },
                                 "operator":"BETWEEN",
                                 "primaryOperandValue":"x",
                                 "secondaryOperandValue":"y"
                              }
                           }
                        ],
                        "children":[
                           {
                              "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                              "operator":"NOT",
                              "nodes":[],
                              "children":[
                                 {
                                    "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                                    "operator":"OR",
                                    "nodes":[
                                       {
                                          "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                          "values":{
                                             "fieldValue":{
                                                "FieldName":"CONTROL_NUMBER",
                                             },
                                             "operator":" > ",
                                             "primaryOperandValue":"30",
                                             "secondaryOperandValue":null
                                          }
                                       }
                                    ],
                                    "children":[]
                                 }
                              ]
                           }
                        ]
                     }
                  ]
               },
               {
                  "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
                  "operator":"OR",
                  "nodes":[
                     {
                        "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                        "values":{
                           "fieldValue":{
                              "FieldName":"EMAIL_ANY_ADDRESS",
                           },
                           "operator":"ENDS_WITH",
                           "primaryOperandValue":"log",
                           "secondaryOperandValue":null
                        }
                     },
                     {
                        "values":{
                           "fieldValue":{
                              "FieldName":"EMAIL_ANY_ADDRESS",
                           },
                           "operator":"BEGINS_WITH",
                           "primaryOperandValue":"log",
                           "secondaryOperandValue":null
                        },
                        "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
                     }
                  ],
                  "children":[
                     
                  ]
               }
            ]
         }
      ];
      pre {
          -moz-tab-size:    4;
          -o-tab-size:      4;
          tab-size:         4;
          font-family: "Courier";
          font-weight: bold;
          color: saddlebrown;
      }
      body {
         background-color: #bbbf3e94;
      }
      &lt;pre class="text" id="output"&gt;&lt;/pre&gt;

      【讨论】:

        【解决方案5】:

        我认为你需要遍历树的 PRE-ORDER 算法

        https://en.wikipedia.org/wiki/Tree_traversal

        更新

        Until all nodes are traversed −
        Step 1 − Visit root node.
        Step 2 − Recursively traverse left subtree.
        Step 3 − Recursively traverse right subtree.
        

        【讨论】:

        • 您能发布一个示例吗?谢谢。
        • 维基百科上的好例子
        猜你喜欢
        • 2012-10-05
        • 2014-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-24
        • 2016-03-27
        • 1970-01-01
        • 2018-05-23
        相关资源
        最近更新 更多