// JSON 数据
const data ={
"nodes": [
{
"id": "aee0e753-3643-452a-9fb5-9d2dda953244",
"type": "semantic_segmentation",
"title": "语义分割",
"subtitle": "语义分割",
"position": {
"x": 49840,
"y": 49810
},
"level": 0,
"branch": 0,
"properties": {
"attrs": {
"model_id": {
"type": "string",
"description": "模型ID",
"required": true,
"name": "模型ID"
}
},
"input_params": {
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"predictions": {
"type": "object",
"description": "分割结果",
"required": true,
"name": "预测结果"
}
}
},
"iconType": "semantic_segmentation"
},
{
"id": "50865899-f9dd-4417-973d-2ccce4c994ef",
"type": "image_clip",
"title": "图片裁剪",
"subtitle": "图片裁剪",
"position": {
"x": 49840,
"y": 49960
},
"level": 1,
"branch": 0,
"properties": {
"attrs": {
"x": {
"type": "number",
"description": "x坐标",
"required": true,
"name": "x坐标"
},
"y": {
"type": "number",
"description": "y坐标",
"required": true,
"name": "y坐标"
},
"width": {
"type": "number",
"description": "宽度",
"required": true,
"name": "宽度"
},
"height": {
"type": "number",
"description": "高度",
"required": true,
"name": "高度"
}
},
"input_params": {
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"image": {
"type": "image",
"description": "裁剪后的图片",
"required": true,
"name": "裁剪后的图片"
}
}
},
"iconType": "image_clip"
},
{
"id": "8ca5e332-3889-40bc-9991-c68ded5af390",
"type": "visualize",
"title": "推理结果可视化",
"subtitle": "推理结果可视化",
"position": {
"x": 49840,
"y": 50560
},
"level": 5,
"branch": 0,
"properties": {
"attrs": {
"color": {
"type": "string",
"description": "颜色",
"required": true,
"default": "#00FF00",
"name": "颜色"
},
"line_width": {
"type": "number",
"description": "线宽",
"required": true,
"default": 2,
"name": "线宽"
}
},
"input_params": {
"predictions": {
"type": "object",
"description": "推理结果",
"required": true,
"name": "推理结果"
},
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"image": {
"type": "image",
"description": "可视化结果",
"required": true,
"name": "可视化结果"
}
}
},
"iconType": "visualize"
},
{
"id": "32fda8fa-cdea-48aa-bb4e-ee578a0f5bd5",
"type": "image_clip",
"title": "图片裁剪_1",
"subtitle": "图片裁剪",
"position": {
"x": 49840,
"y": 50110
},
"level": 2,
"branch": 0,
"properties": {
"attrs": {
"x": {
"type": "number",
"description": "x坐标",
"required": true,
"name": "x坐标"
},
"y": {
"type": "number",
"description": "y坐标",
"required": true,
"name": "y坐标"
},
"width": {
"type": "number",
"description": "宽度",
"required": true,
"name": "宽度"
},
"height": {
"type": "number",
"description": "高度",
"required": true,
"name": "高度"
}
},
"input_params": {
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"image": {
"type": "image",
"description": "裁剪后的图片",
"required": true,
"name": "裁剪后的图片"
}
}
},
"iconType": "image_clip"
},
{
"id": "f65b6774-ebeb-4d62-bb52-ca0f2aa69636",
"type": "visualize",
"title": "推理结果可视化_1",
"subtitle": "推理结果可视化",
"position": {
"x": 49840,
"y": 50260
},
"level": 3,
"branch": 0,
"properties": {
"attrs": {
"color": {
"type": "string",
"description": "颜色",
"required": true,
"default": "#00FF00",
"name": "颜色"
},
"line_width": {
"type": "number",
"description": "线宽",
"required": true,
"default": 2,
"name": "线宽"
}
},
"input_params": {
"predictions": {
"type": "object",
"description": "推理结果",
"required": true,
"name": "推理结果"
},
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"image": {
"type": "image",
"description": "可视化结果",
"required": true,
"name": "可视化结果"
}
}
},
"iconType": "visualize"
},
{
"id": "52644f3c-6057-4450-8a1a-6737b477d365",
"type": "semantic_segmentation",
"title": "语义分割_1",
"subtitle": "语义分割",
"position": {
"x": 49840,
"y": 50410
},
"level": 4,
"branch": 0,
"properties": {
"attrs": {
"model_id": {
"type": "string",
"description": "模型ID",
"required": true,
"name": "模型ID"
}
},
"input_params": {
"image": {
"type": "image",
"description": "图片",
"required": true,
"name": "图片"
}
},
"output_params": {
"predictions": {
"type": "object",
"description": "分割结果",
"required": true,
"name": "预测结果"
}
}
},
"iconType": "semantic_segmentation"
}
],
"connections": [
{
"id": "input-conn",
"from": "input",
"to": "aee0e753-3643-452a-9fb5-9d2dda953244"
},
{
"id": "19b53f7e-6093-4fb5-ba85-93a792b65cfc",
"from": "aee0e753-3643-452a-9fb5-9d2dda953244",
"to": "50865899-f9dd-4417-973d-2ccce4c994ef"
},
{
"id": "6484a860-c0ed-4958-b0ac-9e8d1fc91072",
"from": "8ca5e332-3889-40bc-9991-c68ded5af390",
"to": "output"
},
{
"id": "be0df973-193f-460b-9b7d-1bcecb49f68c",
"from": "50865899-f9dd-4417-973d-2ccce4c994ef",
"to": "32fda8fa-cdea-48aa-bb4e-ee578a0f5bd5"
},
{
"id": "f7e1e6a2-fbc4-4776-bf8f-7b55020e16be",
"from": "32fda8fa-cdea-48aa-bb4e-ee578a0f5bd5",
"to": "f65b6774-ebeb-4d62-bb52-ca0f2aa69636"
},
{
"id": "4732cd83-702b-4973-8ef0-1aa90d2c3113",
"from": "f65b6774-ebeb-4d62-bb52-ca0f2aa69636",
"to": "52644f3c-6057-4450-8a1a-6737b477d365"
},
{
"id": "8cf75af7-9114-454b-9cac-c8a4a2637420",
"from": "52644f3c-6057-4450-8a1a-6737b477d365",
"to": "8ca5e332-3889-40bc-9991-c68ded5af390"
}
]
}
// 原始数据
// 将 nodes 转换为以 id 为键的 Map,便于快速查找
const nodesMap = new Map(data.nodes.map(node => [node.id, node]));
/**
* 递归查找指定节点的上游节点(满足 output_params 包含目标属性)
* @param {string} currentNodeId 当前节点 ID(初始为目标节点 ID)
* @param {string} targetParam 需要检查的 output_params 属性名(如 'image' 或 'predictions')
* @param {Set} visited 已访问的节点 ID 集合(防止循环)
* @param {Array} result 结果数组(存储符合条件的上游节点)
* @returns {Array} 所有符合条件的上游节点
*/
function findUpstreamNodesByOutputParam(
currentNodeId,
targetParam,
visited = new Set(),
result = []
) {
// 1. 找到当前节点的所有上游连接(即 connections 中 to === currentNodeId 的记录)
const incomingConnections = data.connections.filter(conn => conn.to === currentNodeId);
// 2. 遍历所有上游连接,处理每个上游节点
for (const conn of incomingConnections) {
const fromNodeId = conn.from; // 上游节点 ID
// 3. 跳过输入源(from 为 "input" 的情况)
if (fromNodeId === 'input') continue;
// 4. 检查上游节点是否存在(避免无效节点)
if (!nodesMap.has(fromNodeId)) continue;
const fromNode = nodesMap.get(fromNodeId); // 获取上游节点对象
// 5. 避免重复处理(循环引用防护)
if (visited.has(fromNodeId)) continue;
visited.add(fromNodeId); // 标记为已访问
// 6. 检查上游节点的 output_params 是否包含目标属性
if (fromNode.properties?.output_params?.[targetParam]) {
result.push(fromNode); // 符合条件则加入结果
}
// 7. 递归查找该上游节点的上游节点(继续向上遍历)
findUpstreamNodesByOutputParam(fromNodeId, targetParam, visited, result);
}
return result;
}
// --------------------------
// 使用示例:查找两种类型的节点
// --------------------------
// 目标节点 ID(示例中的可视化节点)
const targetNodeId = '8ca5e332-3889-40bc-9991-c68ded5af390';
// 查找 output_params 包含 "predictions" 的上游节点(如语义分割、语义分割_1)
const predictionsUpstreamNodes = findUpstreamNodesByOutputParam(
targetNodeId,
'predictions'
);
// 查找 output_params 包含 "image" 的上游节点(如图片裁剪、图片裁剪_1、推理结果可视化_1)
const imageUpstreamNodes = findUpstreamNodesByOutputParam(
targetNodeId,
'image'
);
console.log('找到 predictions 上游节点:', predictionsUpstreamNodes);
console.log('找到 image 上游节点:', imageUpstreamNodes);
一个递归案例
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
推荐阅读更多精彩内容
- 文科生快速入门python(十二) | 经典的函数递归案例 今天,数据猿重点整理了python的递归函数相关内容,...
- 源代码: 这段代码定义了一个 TypeScript 的类型工具 RecursivePartial<T>,其主要功能...
- 今天我们继续使用 Vue 的撸我们的实战项目,只有在实战中我们才会领悟更多,光纸上谈兵然并卵,继上篇我们的 《Vu...
