【问题标题】:Convert array to nested JSON object - Angular Material tree将数组转换为嵌套的 JSON 对象 - Angular 材质树
【发布时间】:2021-05-28 12:08:10
【问题描述】:

我正在拼命地尝试从 JSON 嵌套格式的角度树中获取选定的节点。到目前为止,我设法使用this.checklistSelection.selected 获得了选定的平面节点数组。但我需要的是,我需要以 JSON 格式获取所选节点,以及所有嵌套 JSON 对象的级别。

[{item: "Risk Analysis", level: 0, expandable: true}
,{item: "Standard", level: 1, expandable: true}
,{item: "Active", level: 2, expandable: true}
,{item: "Volatility", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}
,{item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}
,{item: "Benchmark", level: 2, expandable: true}
,{item: "Volatility", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}
,{item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}
,{item: "Portfolio", level: 2, expandable: true}
,{item: "Volatility", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}
,{item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true}
,{item: "Contribution", level: 4, expandable: true}
,{item: "Total", level: 5, expandable: false}
,{item: "Systematic", level: 5, expandable: false}
,{item: "Specific", level: 5, expandable: false}]

预期:

"Risk Analysis": {
      "Standard": {
        "Active": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        },
        "Portfolio": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        },
        "Benchmark": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        }
      }
    }
  }

如果有 Mat tree 提供的方法,或者任何类型的函数可以做到这一点,有人可以指出我吗?

提前致谢:)

【问题讨论】:

  • 原始对象中的“波动性”在哪里?

标签: javascript json angular angular-material tree


【解决方案1】:

为了构建树,您需要通过为每个项目分配 ID 来预处理数据。您可以在分配关系时使用堆栈来跟踪它们。

您可以分阶段完成:

  1. 为每个项目分配 idparentId 键 (applyRelationships)
  2. 将平面数组转换为树 (listToTree)
  3. 将树转换为对象 (treeToObject)

在原始示例中,我通过设置最大深度来强制嵌套每个对象。我没有使用expandable 属性。在这个修改后的示例中,我放弃了 maxDepth 参数。

const main = () => {
  useCases.forEach(({ data, expected }) => {
    const actual = buildTreeObject(data);
    console.log(JSON.stringify(actual) === JSON.stringify(expected));
    console.log(actual);
  });
};

const useCases = [{
  data: [
    { item: "Risk Analysis", level: 0, expandable: true },
    { item: "Volatility", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false },
    { item: "TaR (68%, 1 year)", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false },
    { item: "VaR (95%, 2 weeks, Chebyshev)", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false }
  ],
  expected: {
    "Risk Analysis": {
      "Volatility": ["Total", "Systematic", "Specific"],
      "TaR (68%, 1 year)": ["Total", "Systematic", "Specific"],
      "VaR (95%, 2 weeks, Chebyshev)": ["Total", "Systematic", "Specific"]
    }
  }
}, {
  data: [
    { item: "Risk Analysis", level: 0, expandable: true },
    { item: "Standard", level: 1, expandable: true },
    { item: "Active", level: 2, expandable: true },
    { item: "Volatility", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false }
  ],
  expected: {
    "Risk Analysis": {
      "Standard": {
        "Active": {
          "Volatility": {
            "Contribution": [ "Total", "Systematic", "Specific" ]
          }
        }
      }
    }
  }
}, {
  data: [
    { item: "Risk Analysis", level: 0, expandable: true },
    { item: "Standard", level: 1, expandable: true },
    { item: "Active", level: 2, expandable: true },
    { item: "Volatility", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false },
    { item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false },
    { item: "Benchmark", level: 2, expandable: true },
    { item: "Volatility", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false },
    { item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false },
    { item: "Portfolio", level: 2, expandable: true },
    { item: "Volatility", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false },
    { item: "VaR (95%, 2 weeks, Chebyshev)", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false }
  ],
  expected: {
    "Risk Analysis": {
      "Standard": {
        "Active": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        },
        "Benchmark": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        },
        "Portfolio": {
          "Volatility": {
            "Contribution": ["Total", "Systematic", "Specific"]
          },
          "VaR (95%, 2 weeks, Chebyshev)": {
            "Contribution": ["Total", "Systematic", "Specific"]
          }
        },
      }
    }
  }
}];

const applyRelationships = (data) => {
  let levelStack = [], lastNode = null;
  return data.map((curr, index) => {
    const node = { ...curr, id: index + 1 };
    if (levelStack.length === 0) {
      levelStack.push({ level: node.level, parent: 0 });
    } else {
      const last = levelStack[levelStack.length - 1];
      if (node.level > last.level) {
        levelStack.push({ level: node.level, parent: lastNode.id });
      } else if (node.level < last.level) {
        const
          levelDiff = last.level - node.level - 1,
          lastIndex = levelStack.length - 1;
        levelStack.splice(lastIndex - levelDiff, lastIndex);
      }
    }
    node.parentId = levelStack[levelStack.length - 1].parent;
    lastNode = node;
    return node;
  });
};

const listToTree = (arr = []) => {
   let indexMap = new Map();
   arr.forEach((node, index) => {
      indexMap.set(node.id, index)
      node.children = [];
   });
   return arr.reduce((res, node, index, all) => {
      if (node.parentId === 0) return [...res, node];
      all[indexMap.get(node.parentId)].children.push(node);
      return res;
   }, []);
};

const treeToObject = (tree = [], result = {}) => {
  tree.forEach(child => {
    if (!child.expandable) {
      result.push(child.item);
    } else {
      const childrenAllEmpty = child.children
        .every(({ children }) => children.length === 0);
      result[child.item] = childrenAllEmpty ? [] : {};
      treeToObject(child.children, result[child.item]);
    }
  });
  return result;
};

const buildTreeObject = (arr = []) =>
  treeToObject(listToTree(applyRelationships(arr)));
  
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

原始回复

const main = () => {
  useCases.forEach(({ data, params: { maxDepth }, expected }) => {
    const actual = buildTreeObject(data, maxDepth);
    console.log(JSON.stringify(actual) === JSON.stringify(expected));
    console.log(actual);
  });
};

const useCases = [{
  data: [
    { item: "Risk Analysis", level: 0, expandable: true },
    { item: "Volatility", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false },
    { item: "TaR (68%, 1 year)", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false },
    { item: "VaR (95%, 2 weeks, Chebyshev)", level: 1, expandable: true },
    { item: "Total", level: 2, expandable: false },
    { item: "Systematic", level: 2, expandable: false },
    { item: "Specific", level: 2, expandable: false }
  ],
  params : { maxDepth: 1 },
  expected: {
    "Risk Analysis": {
      "Volatility": ["Total", "Systematic", "Specific"],
      "TaR (68%, 1 year)": ["Total", "Systematic", "Specific"],
      "VaR (95%, 2 weeks, Chebyshev)": ["Total", "Systematic", "Specific"]
    }
  }
}, {
  data: [
    { item: "Risk Analysis", level: 0, expandable: true },
    { item: "Standard", level: 1, expandable: true },
    { item: "Active", level: 2, expandable: true },
    { item: "Volatility", level: 3, expandable: true },
    { item: "Contribution", level: 4, expandable: true },
    { item: "Total", level: 5, expandable: false },
    { item: "Systematic", level: 5, expandable: false },
    { item: "Specific", level: 5, expandable: false }
  ],
  params: { maxDepth: 4 },
  expected: {
    "Risk Analysis": {
      "Standard": {
        "Active": {
          "Volatility": {
            "Contribution": [ "Total", "Systematic", "Specific" ]
          }
        }
      }
    }
  }
}];

const applyRelationships = (data) => {
  let levelStack = [], lastNode = null;
  return data.map((curr, index) => {
    const node = { ...curr, id: index + 1 };
    if (levelStack.length === 0) {
      levelStack.push({ level: node.level, parent: 0 });
    } else {
      const last = levelStack[levelStack.length - 1];
      if (node.level > last.level) {
        levelStack.push({ level: node.level, parent: lastNode.id });
      } else if (node.level < last.level) {
        levelStack.pop();
      }
    }
    node.parentId = levelStack[levelStack.length - 1].parent;
    lastNode = node;
    return node;
  });
};

const listToTree = (arr = []) => {
   let indexMap = new Map();
   arr.forEach((node, index) => {
      indexMap.set(node.id, index)
      node.children = [];
   });
   return arr.reduce((res, node, index, all) => {
      if (node.parentId === 0) return [...res, node];
      all[indexMap.get(node.parentId)].children.push(node);
      return res;
   }, []);
};

const treeToObject = (tree, maxDepth = 1, result = {}) => {
  tree.forEach(child => {
    result[child.item] = {};
    if (child.level >= maxDepth) {
      result[child.item] = child.children.map(({ item }) => item);
    } else {
      treeToObject(child.children, maxDepth, result[child.item]);
    }
  });
  return result;
};

const buildTreeObject = (arr = [], maxDepth = 1) =>
  treeToObject(listToTree(applyRelationships(arr)), maxDepth);
  
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

【讨论】:

  • 还有一件事可以扩展这些方法以支持更多级别的嵌套。就像将此数组转换为基于 'level' 属性的多级 JSON 对象 [{item: "Risk Analysis", level: 0, expandable: true}, {item: "Standard", level: 1, expandable: true}, {项目:“活跃”,级别:2,可扩展:真},{项目:“波动性”,级别:3,可扩展:真},{项目:“贡献”,级别:4,可扩展:真},{项目: “总计”,级别:5,可扩展:false},{item:“系统”,级别:5,可扩展:false},{item:“特定”,级别:5,可扩展:false}]
  • @NatasaMarinkovic 我要调整maxDepth 参数。不知道为什么它没有走得更远。
  • 我认为当嵌套深度超过 3 级时,applyRelationships () 没有生成正确的 parentID。让我知道你是否成功调整它。谢谢你的帮助,我真的很感激!
  • @NatasaMarinkovic 我修好了。我存储了对前一个节点的引用以访问其 ID。我在代码的最后添加了示例调用。我将maxDepth 设置为4
  • 哦,我仍然无法让它与更复杂的数组一起使用。最好不要依赖最大深度参数,因为我无法提前知道 JSON 对象的最大深度。请在更新后的问题中尝试这个。
猜你喜欢
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 2019-03-28
  • 2017-06-08
  • 1970-01-01
  • 2021-09-27
相关资源
最近更新 更多