使用组节点从多维子数组创建字符串
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
运算符,则此容器将成为一个组,并且其中将包含另一个容器,其中包含 AND
或 OR
用户可以在NOT
、AND
、OR
之间进行选择,但如果他们尝试对它们进行分组,则不能使用相同的运算符,这仅适用于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;
<pre class="text" id="output"></pre>
【讨论】:
【参考方案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.
【讨论】:
您能发布一个示例吗?谢谢。 ***上的好例子以上是关于使用组节点从多维子数组创建字符串的主要内容,如果未能解决你的问题,请参考以下文章