使用组节点从多维子数组创建字符串

Posted

技术标签:

【中文标题】使用组节点从多维子数组创建字符串【英文标题】: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" 字符串要在操作员之间打印。 【参考方案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.

【讨论】:

您能发布一个示例吗?谢谢。 ***上的好例子

以上是关于使用组节点从多维子数组创建字符串的主要内容,如果未能解决你的问题,请参考以下文章

带有字符串子字符串的SwiftUI 5.5初始化数组? [关闭]

数组篇在python中如何查找最长字符串子串

jQuery循环遍历多维数组并显示每个父数组的子数组

从字符串创建多维数组[重复]

使用php从多维数组中获取子数组

从两个数组创建多维数组