You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中实现动态JSON转逻辑公式的函数?

修正TypeScript递归方法:将动态JSON转换为逻辑公式

需求说明

需要实现一个TypeScript递归方法,将符合以下接口结构的动态JSON转换为逻辑公式:

类型定义

export interface Regole {
    descrizioneRegola: string;
    naturaGiuridica: string;
    regolaPerCollegamento: RegolaPerCollegamento[];
    or?: OperatoreLogico;
    and?: OperatoreLogico;
    not?: OperatoreLogico;    
}

export interface OperatoreLogico {
    regolaPerCollegamento?: RegolaPerCollegamento[];
    or?: OperatoreLogico;
    and?: OperatoreLogico;
    not?: OperatoreLogico;
}

export interface RegolaPerCollegamento {
    tipoCollegamentoEsistente?: number;
    tipoCollegamento?: number;
    quantita?: string;
    minimo?: number;
    massimo?: number;
    esattamente?: number;
}       

转换规则

  • and运算符:将数组内的对象用&&连接
  • or运算符:将数组内的对象用||连接
  • not运算符:在子结果前添加!
  • 外层运算符连接内层计算结果,多子项自动添加括号保证逻辑优先级

示例

输入JSON:

let json: OperatoreLogico = {
  "or": {
    "and": {
      "regolaPerCollegamento": [
        {
          "tipoCollegamentoEsistente": 115,
          "quantita": "NESSUNO"
        },
        {
          "tipoCollegamentoEsistente": 118,
          "quantita": "NESSUNO"
        }
      ]
    },
    "or": {
      "regolaPerCollegamento": [
        {
          "tipoCollegamento": 115,
          "minimo": 1
        },
        {
          "tipoCollegamento": 118,
          "minimo": 1
        }
      ]
    }
  }
}

期望输出:

({tipoCollegamentoEsistente: 115, quantita: "NESSUNO"} && {tipoCollegamentoEsistente: 118, quantita: "NESSUNO"}) || ({tipoCollegamento: 115, minimo: 1} || {tipoCollegamento: 118, minimo: 1})

原代码问题分析

原递归方法存在以下关键问题:

  1. 递归调用的返回值未被收集,直接返回空字符串导致结果丢失
  2. initial和result参数维护逻辑混乱,未正确传递运算符上下文
  3. 规则数组的连接运算符逻辑错误,未根据父节点运算符动态调整
  4. 未处理not运算符,且多子节点的括号包裹逻辑缺失

修正后的递归方法

export function convertToRule(root: OperatoreLogico): string {
    // 将单个规则对象转换为格式化字符串
    const formatRuleItem = (item: RegolaPerCollegamento): string => {
        const entries = Object.entries(item).map(([key, value]) => {
            // 字符串类型值加双引号,数字类型直接输出
            const formattedValue = typeof value === 'string' ? `"${value}"` : value;
            return `${key}: ${formattedValue}`;
        });
        return `{${entries.join(', ')}}`;
    };

    // 递归处理节点,parentOp为当前节点所属的父运算符
    const traverse = (node: OperatoreLogico, parentOp?: 'and' | 'or' | 'not'): string => {
        const parts: string[] = [];

        // 处理规则数组
        if (node.regolaPerCollegamento) {
            const ruleItems = Array.isArray(node.regolaPerCollegamento) 
                ? node.regolaPerCollegamento 
                : [node.regolaPerCollegamento];
            if (ruleItems.length > 0) {
                const connector = parentOp === 'and' ? ' && ' : parentOp === 'or' ? ' || ' : '';
                const ruleStrings = ruleItems.map(formatRuleItem);
                parts.push(ruleStrings.length === 1 ? ruleStrings[0] : `(${ruleStrings.join(connector)})`);
            }
        }

        // 处理逻辑运算符节点
        const operatorEntries = Object.entries(node).filter(([key]) => ['and', 'or', 'not'].includes(key)) as Array<['and' | 'or' | 'not', OperatoreLogico | OperatoreLogico[]]>;
        
        for (const [op, childNodes] of operatorEntries) {
            const children = Array.isArray(childNodes) ? childNodes : [childNodes];
            const childResults = children.map(child => traverse(child, op));
            
            let combined: string;
            if (op === 'not') {
                combined = childResults.map(res => `!${res}`).join(' ');
            } else {
                const connector = op === 'and' ? ' && ' : ' || ';
                combined = childResults.length === 1 ? childResults[0] : `(${childResults.join(connector)})`;
            }
            parts.push(combined);
        }

        // 拼接当前节点的所有子项结果
        if (parts.length === 0) return '';
        if (parts.length === 1) return parts[0];

        const connector = parentOp === 'and' ? ' && ' : parentOp === 'or' ? ' || ' : ' && ';
        return `(${parts.join(connector)})`;
    };

    // 处理根节点:识别根节点的外层运算符
    const rootOps = Object.keys(root).filter(key => ['and', 'or', 'not'].includes(key)) as Array<'and' | 'or' | 'not'>;
    if (rootOps.length === 1) {
        const op = rootOps[0];
        const childNodes = root[op]!;
        const children = Array.isArray(childNodes) ? childNodes : [childNodes];
        const childResults = children.map(child => traverse(child, op));
        const connector = op === 'and' ? ' && ' : op === 'or' ? ' || ' : ' ';
        return childResults.length === 1 ? childResults[0] : `(${childResults.join(connector)})`;
    } else {
        return traverse(root);
    }
}

// 测试示例
const json: OperatoreLogico = {
    "or": {
        "and": {
            "regolaPerCollegamento": [
                {
                    "tipoCollegamentoEsistente": 115,
                    "quantita": "NESSUNO"
                },
                {
                    "tipoCollegamentoEsistente": 118,
                    "quantita": "NESSUNO"
                }
            ]
        },
        "or": {
            "regolaPerCollegamento": [
                {
                    "tipoCollegamento": 115,
                    "minimo": 1
                },
                {
                    "tipoCollegamento": 118,
                    "minimo": 1
                }
            ]
        }
    }
};

console.log(convertToRule(json));

代码修正说明

  1. 分层递归逻辑:

    • 新增traverse内部函数专门处理递归,通过parentOp参数传递父节点运算符,明确子项的连接规则
    • 根节点单独处理,先识别外层运算符再递归处理子节点
  2. 结果收集与拼接:

    • 用parts数组收集当前节点的所有子项结果,避免原代码中返回值丢失的问题
    • 递归调用的返回值被正确收集并参与后续拼接
  3. 规则格式化优化:

    • 单独提取formatRuleItem函数,统一处理规则对象到字符串的转换,自动区分字符串和数字类型的格式化方式
  4. 运算符逻辑完善:

    • 正确实现and/or/not三种运算符的连接逻辑
    • 支持单个或数组形式的子节点,兼容原代码的数组处理逻辑
    • 自动为多子项添加括号,保证逻辑运算的优先级
  5. 边界情况处理:

    • 处理单个规则项的无括号场景
    • 处理空规则数组的空字符串返回
    • 处理根节点无外层运算符的直接遍历场景

内容的提问来源于stack exchange,提问作者travis_91

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:49:52