如何在JavaScript中基于带AND/OR的条件字符串筛选JSON数组
解决方案:JavaScript处理带逻辑运算符和嵌套括号的条件筛选
针对你的需求,这里提供两种可行的实现思路,分别适用于不同场景:
方法一:将条件字符串转换为JS表达式(简单快速)
核心思路是把你的条件字符串转换成合法的JavaScript逻辑表达式,通过函数执行来判断每个数据项是否符合条件。同时需要处理数据中键名大小写、空格不一致的问题。
实现代码
// 归一化对象键名:转小写+去除首尾空格 function normalizeObj(obj) { const normalized = {}; Object.keys(obj).forEach(key => { const normalizedKey = key.trim().toLowerCase(); normalized[normalizedKey] = obj[key]; }); return normalized; } // 将条件字符串转换为可执行的JS表达式 function convertCondition(conditionStr) { // 替换转义引号 let expr = conditionStr.replace(/"/g, '"'); // 替换 "Key": "Value" 为 normalizedObj['归一化Key'] === 'Value' expr = expr.replace(/"([^"]+)":\s*"([^"]+)"/g, (match, rawKey, value) => { const normalizedKey = rawKey.trim().toLowerCase(); // 转义字符串中的单引号,避免语法错误 const escapedValue = value.replace(/'/g, "\\'"); return `normalizedObj['${normalizedKey}'] === '${escapedValue}'`; }); // 替换逻辑运算符 expr = expr.replace(/\bAND\b/g, '&&').replace(/\bOR\b/g, '||'); return expr; } // 筛选函数 function filterData(data, conditionStr) { try { const conditionExpr = convertCondition(conditionStr); // 生成判断函数,避免重复编译 const testFn = new Function('normalizedObj', `return ${conditionExpr};`); return data.filter(item => { const normalized = normalizeObj(item); return testFn(normalized); }); } catch (err) { console.error('条件解析失败:', err); return []; } } // 测试示例 const Data = [ {Application: 'APIM', Owner: 'Vande Mataram','Customer Code': 'DJLFKASJF',Environment: 'Dev',OS: 'Linux',Region: 'EU','TEAM ': 'DemoTestCOE'}, {TEAM: 'BCC'}, {Team: 'DemoTestCOE','Customer Code': 'DJLFKASJF',Owner: 'Vande Mataram',Environment: 'Prod',Region: 'EU',Application: 'Ui-Path',OS: 'Windows'}, {Application: 'MGMT','Customer Code': 'DJLFKASJF', os: 'Windows','Owner ': 'Vande Mataram', Region: 'East US 2',Team: 'DemoTestCOE',Environment: 'MGMT' }, {Application: 'PIER',Owner: 'Govindarajulu Balaji',OS: 'Windows',Region: 'DemoTestCOE', Environment: 'QA'} ]; const condition = '"Owner": "Vande Mataram" AND ("Application": "APIM" AND "Customer Code": "DJLFKASJF") OR ("Owner": "Vande Mataram" AND "Customer Code": "DJLFKASJF") OR "Owner": "Vande Mataram"'; console.log(filterData(Data, condition)); // 输出:第1、3、4条数据
优缺点
- 优点:代码简单易实现,处理速度快
- 缺点:如果条件字符串来自不可信来源(比如用户输入),存在代码注入风险,仅适用于条件可控的场景
方法二:递归解析条件表达式(安全可靠)
如果需要处理不可控的条件字符串,建议通过词法分析+语法分析构建抽象语法树(AST),再遍历AST进行条件判断,完全避免eval或动态函数的安全风险。
核心步骤
- 词法分析(Tokenize):将条件字符串拆分为可识别的标记(括号、运算符、键、值等)
- 语法分析(Parse):基于标记构建AST,处理嵌套括号和运算符优先级(AND优先级高于OR)
- 求值(Evaluate):遍历AST,对每个归一化后的 data item 进行条件判断
简化实现示例
// 归一化对象键名 function normalizeObj(obj) { const normalized = {}; Object.keys(obj).forEach(key => { normalized[key.trim().toLowerCase()] = obj[key]; }); return normalized; } // 词法分析:生成tokens function tokenize(conditionStr) { const str = conditionStr.replace(/"/g, '"'); const tokens = []; let i = 0; const len = str.length; while (i < len) { const char = str[i]; if (char === '(' || char === ')') { tokens.push({ type: 'PAREN', value: char }); i++; } else if (/^\s$/.test(char)) { i++; } else if (char === '"') { i++; let strVal = ''; while (i < len && str[i] !== '"') { strVal += str[i]; i++; } tokens.push({ type: 'STRING', value: strVal }); i++; } else if (str.slice(i, i+3) === 'AND') { tokens.push({ type: 'OPERATOR', value: 'AND' }); i +=3; } else if (str.slice(i, i+2) === 'OR') { tokens.push({ type: 'OPERATOR', value: 'OR' }); i +=2; } else if (char === ':') { tokens.push({ type: 'COLON', value: ':' }); i++; } else { throw new Error(`无效字符: ${char} 位置: ${i}`); } } return tokens; } // 语法分析:构建AST function parse(tokens) { let current = 0; function walk() { let token = tokens[current]; // 处理括号包裹的子表达式 if (token.type === 'PAREN' && token.value === '(') { current++; token = tokens[current]; const node = { type: 'GROUP', expression: walk() }; if (tokens[current].type !== 'PAREN' || tokens[current].value !== ')') { throw new Error('缺失闭合括号'); } current++; return node; } // 处理键值判断条件 if (token.type === 'STRING') { const key = token.value; current++; if (tokens[current].type !== 'COLON') { throw new Error('缺失冒号'); } current++; if (tokens[current].type !== 'STRING') { throw new Error('值必须是字符串'); } const value = tokens[current].value; current++; return { type: 'CONDITION', key: key.trim().toLowerCase(), value }; } // 处理逻辑表达式 let node = { type: 'EXPRESSION', left: walk(), operator: null, right: null }; while (current < tokens.length && tokens[current].type === 'OPERATOR') { node.operator = tokens[current].value; current++; node.right = walk(); // 处理运算符优先级:AND优先于OR,这里通过嵌套表达式实现 if (node.operator === 'OR' && current < tokens.length && tokens[current].value === 'AND') { const temp = node; node = { type: 'EXPRESSION', left: temp.left, operator: temp.operator, right: walk() }; } } return node; } const ast = walk(); if (current !== tokens.length) { throw new Error('条件字符串解析不完整'); } return ast; } // 求值:遍历AST判断数据项是否符合条件 function evaluate(ast, normalizedObj) { switch (ast.type) { case 'CONDITION': return normalizedObj[ast.key] === ast.value; case 'GROUP': return evaluate(ast.expression, normalizedObj); case 'EXPRESSION': const left = evaluate(ast.left, normalizedObj); const right = evaluate(ast.right, normalizedObj); if (ast.operator === 'AND') { return left && right; } else if (ast.operator === 'OR') { return left || right; } throw new Error(`未知运算符: ${ast.operator}`); default: throw new Error(`未知节点类型: ${ast.type}`); } } // 筛选函数 function safeFilterData(data, conditionStr) { try { const tokens = tokenize(conditionStr); const ast = parse(tokens); return data.filter(item => { const normalized = normalizeObj(item); return evaluate(ast, normalized); }); } catch (err) { console.error('条件解析失败:', err); return []; } } // 测试 console.log(safeFilterData(Data, condition)); // 输出:第1、3、4条数据
优缺点
- 优点:完全避免代码注入风险,支持复杂嵌套逻辑,适合不可控的条件输入场景
- 缺点:实现复杂度较高,需要处理词法、语法分析的细节
内容的提问来源于stack exchange,提问作者Venkat
相关产品推荐
相关产品推荐

