将平面对象数组转换为嵌套对象

2023-12-25

我有以下数组(实际上来自后端服务):

const flat: Item[] = [
    { id: 'a', name: 'Root 1', parentId: null },
    { id: 'b', name: 'Root 2', parentId: null },
    { id: 'c', name: 'Root 3', parentId: null },

    { id: 'a1', name: 'Item 1', parentId: 'a' },
    { id: 'a2', name: 'Item 1', parentId: 'a' },

    { id: 'b1', name: 'Item 1', parentId: 'b' },
    { id: 'b2', name: 'Item 2', parentId: 'b' },
    { id: 'b2-1', name: 'Item 2-1', parentId: 'b2' },
    { id: 'b2-2', name: 'Item 2-2', parentId: 'b2' },
    { id: 'b3', name: 'Item 3', parentId: 'b' },

    { id: 'c1', name: 'Item 1', parentId: 'c' },
    { id: 'c2', name: 'Item 2', parentId: 'c' }
];

where Item is:

interface Item {
    id: string;
    name: string;
    parentId: string;
};

为了与显示树(类似文件夹)视图的组件兼容,需要将其转换为:

const treeData: NestedItem[] = [
    {
        id: 'a',
        name: 'Root 1',
        root: true,
        count: 2,
        children: [
          {
            id: 'a1',
            name: 'Item 1'
          },
          {
            id: 'a2',
            name: 'Item 2'
          }
        ]
    },
    {
        id: 'b',
        name: 'Root 2',
        root: true,
        count: 5, // number of all children (direct + children of children)
        children: [
          {
            id: 'b1',
            name: 'Item 1'
          },
          {
            id: 'b2',
            name: 'Item 2',
            count: 2,
            children: [
                { id: 'b2-1', name: 'Item 2-1' },
                { id: 'b2-2', name: 'Item 2-2' },
            ]
          },
          {
            id: 'b3',
            name: 'Item 3'
          },
        ]
    },
    {
        id: 'c',
        name: 'Root 3',
        root: true,
        count: 2,
        children: [
          {
            id: 'c1',
            name: 'Item 1'
          },
          {
            id: 'c2',
            name: 'Item 2'
          }
        ]
    }
];

where NestedItem is:

interface NestedItem {
    id: string;
    name: string;
    root?: boolean;
    count?: number;
    children?: NestedItem[];
}

到目前为止我所尝试过的都是这样的:

// Get roots first
const roots: NestedItem[] = flat
    .filter(item => !item.parentId)
    .map((item): NestedItem => {
        return { id: item.id, name: item.name, root: true }
    });

// Add "children" to those roots
const treeData = roots.map(node => {
    const children = flat
        .filter(item => item.parentId === node.id)
        .map(item => {
            return { id: item.id, name: item.name }
        });
    return {
        ...node,
        children,
        count: node.count ? node.count + children.length : children.length
    }
});

但这当然只能获得第一级子节点(根节点的直接子节点)。它以某种方式需要递归,但我不知道如何实现这一点。


不假设展平数组的顺序或嵌套对象的深度:

Array.prototype.reduce足够灵活来完成这项工作。如果您不熟悉Array.prototype.reduce我建议读这个 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce。您可以通过执行以下操作来实现此目的。

我这里有两个依赖递归的函数:findParent and checkLeftOvers. findParent尝试找到对象父对象并返回true or false根据是否找到它。在我的减速器中,我将当前值添加到剩余数组中,如果findParent回报false. If findParent回报true I call checkLeftOvers查看我的剩余数组中是否有任何对象是该对象的子对象findParent刚刚添加。

注:我添加了{ id: 'b2-2-1', name: 'Item 2-2-1', parentId: 'b2-2'} to the flat数组来证明这将按照您的意愿进行深入。我也重新订购了flat证明这在这种情况下也适用。希望这可以帮助。

const flat = [
    { id: 'a2', name: 'Item 1', parentId: 'a' },
    { id: 'b2-2-1', name: 'Item 2-2-1', parentId: 'b2-2'},
    { id: 'a1', name: 'Item 1', parentId: 'a' },
    { id: 'a', name: 'Root 1', parentId: null },
    { id: 'b', name: 'Root 2', parentId: null },
    { id: 'c', name: 'Root 3', parentId: null },
    { id: 'b1', name: 'Item 1', parentId: 'b' },
    { id: 'b2', name: 'Item 2', parentId: 'b' },
    { id: 'b2-1', name: 'Item 2-1', parentId: 'b2' },
    { id: 'b2-2', name: 'Item 2-2', parentId: 'b2' },
    { id: 'b3', name: 'Item 3', parentId: 'b' },
    { id: 'c1', name: 'Item 1', parentId: 'c' },
    { id: 'c2', name: 'Item 2', parentId: 'c' }
];

function checkLeftOvers(leftOvers, possibleParent){
  for (let i = 0; i < leftOvers.length; i++) {
    if(leftOvers[i].parentId === possibleParent.id) {
      delete leftOvers[i].parentId
      possibleParent.children ? possibleParent.children.push(leftOvers[i]) : possibleParent.children = [leftOvers[i]]
      possibleParent.count = possibleParent.children.length
      const addedObj = leftOvers.splice(i, 1)
      checkLeftOvers(leftOvers, addedObj[0])
    }
  }
}

function findParent(possibleParents, possibleChild) {
  let found = false
  for (let i = 0; i < possibleParents.length; i++) {
    if(possibleParents[i].id === possibleChild.parentId) {
      found = true
      delete possibleChild.parentId
      if(possibleParents[i].children) possibleParents[i].children.push(possibleChild)
      else possibleParents[i].children = [possibleChild]
      possibleParents[i].count = possibleParents[i].children.length
      return true
    } else if (possibleParents[i].children) found = findParent(possibleParents[i].children, possibleChild)
  } 
  return found;
}
 
 const nested = flat.reduce((initial, value, index, original) => {
   if (value.parentId === null) {
     if (initial.left.length) checkLeftOvers(initial.left, value)
     delete value.parentId
     value.root = true;
     initial.nested.push(value)
   }
   else {
      let parentFound = findParent(initial.nested, value)
      if (parentFound) checkLeftOvers(initial.left, value)
      else initial.left.push(value)
   }
   return index < original.length - 1 ? initial : initial.nested
 }, {nested: [], left: []})
 
console.log(nested)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将平面对象数组转换为嵌套对象 的相关文章

  • 从提交的表单中获取值

    我有一个非常简单的表格
  • Javascript - 删除粘贴上的空格

    我有一个最大长度为 10 的输入文本字段 该字段用于澳大利亚电话号码 10 位数字 电话号码通常分为以下语法 12 12345678 如果有人复制上面的内容并将其粘贴到我的输入字段中 显然会留下最后一位数字并保留空格 有没有办法在粘贴到输入
  • 如何使用 JavaScript 访问 runat="server" ASP 元素?

    似乎每个人都在这样做 在代码帖子等中 但我不知道如何 每当我尝试使用 JavaScript 操作 asp 元素时 我都会得到一个 element is null or document is undefined 等等错误 JavaScrip
  • 使用 JavaScript 设置文本区域样式

    我对 JavaScript 完全陌生 想修改表单的文本区域 由外部脚本生成 如下所示 1 开始时的文本区域 标记为 您的消息 颜色为 rgb 136 136 136 2 焦点上的文本区域 标签已删除 颜色设置为 rgb 0 0 0 3 文本
  • 如何在 JavaScript 中将本地化日期转换为标准日期?

    我正在编写一段 JavaScript 代码来对包含日期 包含本地化日期 和其他字段的数据表进行排序 例如 lunes 29 de agosto de 2011 field1 field2 lunes 28 de agosto de 2011
  • 是否有管理 __utma、__utmz 等 cookie 的标准?

    无论我登录 Facebook 还是 Twitter 我都会受到以下名称的 cookie 轰炸 utma utmb utmc utmv 它们的功能是什么 是否有一个标准来管理这些在服务器端的使用方式 这些 cookie 通常与谷歌分析 htt
  • 无法设置未定义的属性“显示”

    我正在编写脚本来隐藏 显示菜单 但遇到了一些麻烦 function displayMenu var classMenu event target className classMenu Menu document getElementsBy
  • 如何从 JQuery - IonRangeSlider 获取值?

    我怎样才能得到低值和高值ion rangeSlider http ionden com a plugins ion rangeSlider en html通过单击按钮来组件 这是我的 jQuery 代码
  • 仅在文件下载完成后设置 cookie。

    我有一个场景 我想告诉用户下载完成并提示关闭按钮 为此 我使用 jquery 插件来连续监视 cookie 以了解下载何时完成 我的问题是我想设置这个cookie fileDownload true and path 下载完成后立即进行 为
  • 多维数组、Vuex 和变异

    我正在尝试添加和删除存储在 Vuex 中的多维数组中的项目 数组是一组类别 每个类别又有一个子类别 无穷大 不是简单的二维数组 示例数据集是这样的 id 123 name technology parent id null children
  • 与 webpack 捆绑后,无法读取枚举的未定义属性

    我有一个 React 库 我想使用 Webpack 来构建它 该库是使用 Typescript 编写的 似乎一切正常 但由于某种原因枚举却不起作用 当我将库安装到我的 React 应用程序中时 我发现Cannot read properti
  • 是否可以使用 JavaScript 导入 HTML?

    我有一些具有相同页脚的 html 页面 使用 JavaScript 并且仅使用 JavaScript 我可以在其中导入另一个 html 页面吗 下面介绍了如何仅使用 JavaScript 向页面添加页脚 2022 代码 使用fetch ht
  • 没有 jQuery 的纯 CSS 工具提示[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用纯 CSS 创建 工具提示尾部 https stackoverflow com questions 5623072 how can i create a tooltip tail using
  • 你可以推荐什么 JavaScript 缓存 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个好的javascript内存缓存库来缓存客户端计算结果 我的要求 适用于 Internet Explorer FireFox
  • 如何在 JS 文件中使用 Github 机密

    我有一个基本的 git 存储库 其中包含用于构建和部署的 github 操作 主要是 HTML 和 TS 文件 但是我必须在一些需要保密的 API 密钥中使用 所以我想办法为他们使用 GITHUB SECRETS 如何在我的 js 或 TS
  • 如何强制传单更新地图?

    当我将 Leaflet 与 React 一起使用时 我遇到了问题 据我研究 问题是 Leaflet 也想控制 DOM 渲染 现在 国家将使用与后端信息相对应的特定颜色代码 范围为1 gt 100 正确着色 但是 它每分钟更新一次 更新后 国
  • 如何使用 JavaScript 大致计算网站的连接速度?

    如何使用 JavaScript 大致计算网站的连接速度 我想创建一个像这样的javascript小部件 它将计算打开当前打开页面的速度 我想问是否可以仅使用 javascript 来完成此操作以及想法是什么 Update 请注意 页面大小始
  • React.js 的状态基于其他状态

    我遇到了 React js 的一些问题 并且在调用 setState 时状态没有立即设置 我不确定是否有更好的方法来解决这个问题 或者这是否真的只是 React 的一个缺点 我有两个状态变量 其中一个基于另一个 原始问题的小提琴 http
  • 按 Chartjs 条形图的键对对象进行分组

    我正在尝试使用 Chart js 创建条形图 我在尝试根据每个用户的状态创建分组条形图时陷入困境 这是数据 statusId 0 firstName Joe status appealed count 1 statusId 0 firstN
  • jQuery - 将日期选择器的容器设置为特定的 div

    我在 div 上使用 jQuery UI 日期选择器 div通过移动鼠标隐藏和显示 因为日期选择器存在于末尾标签 不在我的 div 内 当我将鼠标移动到日期选择器时 div 消失 我像这样加载了日期选择器 JavaScript dt1 da

随机推荐

  • Graphql 帖子正文“必须提供查询字符串。”

    我使用 Express graphql 中间件 我在正文行中发送以下请求 POST graphql HTTP 1 1 Host local 8083 Content Type application graphql Cache Contro
  • 在 Windows 中 grep unicode 文本文件的免费程序? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 unicode 文本文件集合 从 regedit 导出 我想提取带有特定文本的所有行 我试过
  • 用户 root @ localhost 的访问被拒绝 [重复]

    这个问题在这里已经有答案了 我正在尝试连接到 mysql 但它给出了警告 mysqli connect HY000 1045 用户 root localhost 访问被拒绝 使用密码 YES 我认为用户名和密码是正确的 因为我可以在 kom
  • 为什么 `useCallback` 不能总是返回相同的引用

    我不明白为什么useCallback每次更新其中一个 deps 时 总是返回一个新的引用 它导致许多重新渲染React memo 本来可以避免的 此实施有 什么问题 如果有 useCallback export function useCa
  • 结果不一致 (C)?

    在此程序中 我编写了多个公式 对于一个公式 我得到了不一致的结果 尽管每次运行的代码和输入都是相同的 有问题的函数是 WqFormula 在某些运行中 我得到正确的结果 即 0 041667 而在其他运行中 我得到 0 000000 我正在
  • int main() 有什么问题?

    我无法计算我在外面和这里看到的 C 代码的次数 它定义了main as int main 当我编译它时 gcc ansi pedantic Wstrict prototypes Werror foo c 它出错了 foo c 2 warni
  • 编写 Gradle 脚本来运行 Eclipse Android 测试项目的单元测试用例

    我有一个简单的 HelloWorld Android 项目 在 Eclipse IDE 中构建 我能够在该项目的 cmd 提示符中成功执行 gradle build 我还为其编写了一个简单的 JUnit Android 测试项目 并且它在
  • 如何将经度、纬度、高程转换为笛卡尔坐标?

    我下载了天气数据 它有经度 十进制 纬度 十进制 和海拔 米 值 没有有关所使用的坐标系的信息 我如何将其转换为笛卡尔坐标 我的尝试如下 但是 我的问题是找到正确的公式 def cartesian self longitude latitu
  • 如何使用 easywebdav 通过 python 连接到 owncloud?

    我正在尝试连接到owncloud与 python 的实例 我找到了easywebdav这应该可以很容易地通过 webdav 连接 但是当尝试连接时 我收到 404 Not Found import easywebdav webdav eas
  • 如何用CSS实现单行省略号

    我希望能够在响应式设计中添加三个点并将文本保留在一行中 例如 我有一个链接 其中包含容器元素内的链接 例如 span 如果文本很长 它将在小屏幕上分两行显示 This is a very long text and it wraps bec
  • 如何在 JSF 中将对象从一个页面传递到另一个页面而不编写转换器

    首先对我的英语感到抱歉 我在 JSF2 中有两个页面 一个用于列出乘客 另一个用于创建 更新乘客 我还有两个 ViewScoped bean 一个包含乘客列表 另一个用于在 pageB 中保留所选乘客 我看到了通过 viewParam 或
  • 如何在 ASP.NET/C# 中检查 URL 的顶级域?

    假设 www mysite fr home 是 URL 现在如何从中获取 fr 只是 fr 实际上我想做的是在检测到访问者的国家 地区后在运行时更改母版页 是的 我可以使用其他类中存在的countryCode变量 但也许我只能这样做 只是想
  • C#(锐利)从txt文件中读取随机行[重复]

    这个问题在这里已经有答案了 谁能告诉我如何从txt文件中读取随机行 我想从 txt 文件中读取随机行并在文本框中仅显示该行 代码示例会很棒 感谢转发 var lines File ReadAllLines path var r new Ra
  • jQuery UI - 加速自动完成

    我的自动完成功能运行良好 但我正在尝试找出使其更快的方法 这是我的 HTML Country
  • LyX 中的 Zed 表示法

    是否可以在 LyX 中创建 Zed 表示法方案 如何做呢 解决了 我通过 MikTeX 安装了 zed csp 样式包 然后 在 LyX 我去了Document gt Settings gt Latex Preamble并添加了 usepa
  • 如何捕获回滚异常

    对 catch 子句中已存在的 SqlTransaction RollBack 实现错误处理的最佳方法是什么 我的代码大致是这样的 using SqlConnection objSqlConn new SqlConnection connS
  • 如何在C#项目中实现和进行OCR?

    我已经搜索了一段时间 并且看到了一些 OCR 库请求 我想知道如何实现最纯粹 易于安装和使用的 OCR 库 并提供安装到 C 项目的详细信息 如果可能的话 我只想像通常的 dll 引用一样实现它 Example using org pdfb
  • 尝试将 yaml 数据转换为结构时输出为空[重复]

    这个问题在这里已经有答案了 我正在尝试将 yaml 数据转换为结构并打印它 我得到的这个程序的输出是空的 package main import fmt gopkg in yaml v2 type example struct variab
  • 当 size < 1 时,geom_sf 大小参数不起作用

    问题 我正在尝试使用 sf 包创建美国州 县的地图 并且geom sf 来自 ggplot2 但我无法得到size多边形边界线宽度的参数geom sf 正确创建细线时size lt 1 e g ggplot sf obj gt geom s
  • 将平面对象数组转换为嵌套对象

    我有以下数组 实际上来自后端服务 const flat Item id a name Root 1 parentId null id b name Root 2 parentId null id c name Root 3 parentId