ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 607|回复: 9

[求助] 关于父子结构和树结构转化的问题

[复制链接]

TA的精华主题

TA的得分主题

发表于 2023-2-24 09:11 | 显示全部楼层 |阅读模式
image.jpg


Tree画图.zip (210.24 KB, 下载次数: 21)

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-24 12:58 | 显示全部楼层
还是不熟练,先写了一个父子结构转树结构的代码,但是总觉得不是很通用,需要更好的方法。

  1. ''' 父子转标准表测试
  2. Sub Test_DataTable()
  3.     Dim dF As New Dictionary
  4.     Dim dS As New Dictionary
  5.     With Sheet2 'ActiveSheet
  6.         temp = .Range("a1").CurrentRegion.Value
  7.         .Range("b:c").Sort .Range("b1"), xlAscending, , , , , , xlYes
  8.         ar = .Range("a1").CurrentRegion.Value
  9.         .Range("a1:c" & UBound(temp)).Value = temp
  10. '        .Range("e1:g" & UBound(ar)).Value = ar
  11.         
  12.         For i = 2 To UBound(ar)
  13.             dF(ar(i, 2)) = dF(ar(i, 2)) & "|" & ar(i, 3)
  14.             dS(ar(i, 3)) = ar(i, 2)
  15.         Next
  16.         
  17.         ReDim br(1 To UBound(ar), 1 To 11)
  18.         i = 0
  19.         For Each x In dS.Keys
  20.             If Not dF.Exists(x) Then '末级节点
  21.                 i = i + 1
  22.                 s = x: sx = x
  23.                 Do While dS.Exists(s)
  24.                     s = dS(s)
  25.                     sx = s & "|" & sx
  26.                 Loop
  27.                 cr = Split(sx, "|")
  28.                 For j = 0 To UBound(cr)
  29.                     br(i, j + 2) = cr(j)
  30.                 Next
  31.                     br(i, 1) = i
  32.             End If
  33.         Next
  34.         .Range("h2:t65536").ClearContents
  35.         .Range("h2").Resize(UBound(br), UBound(br, 2)).Value = br
  36.         .Range("H1").CurrentRegion.Offset(0, 1).Sort .Range("i1"), xlAscending, .Range("j1"), , xlAscending, .Range("k1"), xlAscending, xlYes
  37.     End With
  38.     Set dS = Nothing
  39.     Set dF = Nothing
  40. End Sub
复制代码


评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2023-2-24 16:26 来自手机 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
递归。。。。

js代码
Screenshot_2023-02-24-16-22-47-609_com.android.chrome.jpg
Screenshot_2023-02-24-16-26-19-160_cn.uujian.browser.jpg

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-24 17:15 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
只需要一个节点和父节点,某个子节点,3个信息一组,自动生成树状图??

我可惜不会转化成VBA,自己写也能写出来,但是第一不通用,第二代码循环太多,逻辑不够简洁。

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-25 14:26 | 显示全部楼层
micch 发表于 2023-2-24 12:58
还是不熟练,先写了一个父子结构转树结构的代码,但是总觉得不是很通用,需要更好的方法。

从末级向父级倒推,得出的数据是乱序的,虽然输出后排序似乎也行,但是不如一次性得到有序的树结构方便。

论坛好多父子结构,树结构的帖子,看来这一类帖子都不吸引人了
头像被屏蔽

TA的精华主题

TA的得分主题

发表于 2023-2-25 16:28 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-25 22:23 | 显示全部楼层
代码很简洁,感谢还都做了注释。我用了200多行,而且分3-4次循环才完成。递归还是太不熟练了,我好好学习一遍

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-25 23:57 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
暂时先做到这一步,关于递归还需要好好理解一下。
现在只能算出固定位置,如果做树状图还需要计算相对位置。
画图的时候节点如何根据相邻节点来判断最佳位置,还想不好如何做。



image.png

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-26 13:04 | 显示全部楼层
递归到末级的时候退出还是不太理解
  1. Dim fD As Dictionary
  2. Sub Father_SontoTree()
  3.     With ActiveSheet
  4.         ar = .Range("A1").CurrentRegion.Value
  5.     End With
  6.     ''' 字典存储父子数据
  7.     Set fD = New Dictionary
  8.     For i = 2 To UBound(ar)
  9.         Father = ar(i, 2)
  10.         Son = ar(i, 3)
  11.         If fD.Exists(Father) Then
  12.         Else
  13.             Set fD(Father) = New Dictionary
  14.         End If
  15.         fD(Father).Add Son, Father
  16.     Next
  17.     [h:z].ClearContents
  18.     [h1:t1].Value = [{"No.","Level1","Level2","Level3","Level4","Level5","Level6","Level7","Level8","Level9","Level10","Level11","Level12"}]
  19.     ''' 遍历字典输出,开始行,开始列,当前列,数据首列
  20.     ExportTree fD, 1, 8, 1, 8
  21. End Sub
  22. Function ExportTree(d As Variant, r0%, c0%, lastC%, firstC%)
  23.     For Each x In d.Keys                ''' 字典中x是子节点
  24.         If fD.Exists(x) Then            '''x同时是父节点,行+1,列+1
  25.             r0 = r0 + 1
  26.             c0 = c0 + 1
  27.             lastC = c0                  ''' 记录父节点的列序号
  28.             Cells(r0, c0).Value = x     ''' 输出到单元格
  29.             ''' 父节点在fD中存在,所以继续对其下属子节点循环
  30.             ExportTree fD(x), r0, c0, lastC, firstC
  31.             fD.Remove x
  32.         Else                            ''' x是末级子节点
  33.             If c0 <= firstC Then Exit Function '''列位置退到初始列以下完成遍历
  34.             r0 = r0 + 1
  35.             c0 = lastC + 1              ''' 末级子节点列序号不再增加,上级父节点列+1
  36.             Cells(r0, c0).Value = x     ''' 输出末级子节点
  37.         End If
  38.     Next
  39.     lastC = lastC - 1   ''' 当前最后一个末级子节点,列位置回退一列
  40.     c0 = lastC
  41. End Function
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-2-26 19:57 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
之前两种写法都需要排序数据,乱序数据不合适,换一种写法

  1. Sub Main_CreateTree()
  2.     Dim ar, Coll As Collection
  3.     ''' 读入父子结构数据数组,一对一
  4.     ar = ReadFatherSon
  5.     ''' 数据整理到节点集合
  6.     Set Coll = Father_SonToCollection(ar)
  7.     ''' 输出节点数据到表格
  8.     PrintTree Coll, Range("h2")
  9. End Sub
  10. Function Father_SonToCollection(ar) As Collection
  11.     Dim fD As Dictionary, sD As Dictionary
  12.     Dim Father$, Son$, i&, j&, x
  13.     Set fD = New Dictionary
  14.     Set sD = New Dictionary
  15.     ''' 父子关系读入字典
  16.     For i = 1 To UBound(ar)
  17.         Father = ar(i, 1)
  18.         Son = ar(i, 2)
  19.         If fD.Exists(Father) Then
  20.         Else
  21.             Set fD(Father) = New Dictionary
  22.         End If
  23.         fD(Father).Add Son, Father
  24.         sD.Add Son, Father
  25.     Next
  26.     ''' 节点整理
  27.     Dim Tree As Collection, Node As ClassNode
  28.     Set Tree = New Collection
  29.     ''' 根节点=============================================================pause
  30.     i = 0: j = 0
  31.     For Each x In fD.Keys
  32.         If Not sD.Exists(x) Then
  33.             i = i + 1 ''' 唯一编号
  34.             j = j + 1 ''' 层内编号
  35.             '''树编码,两位递进默认同一级不超过99个节点
  36.             Set Node = New ClassNode
  37.             Node.addDetail i, 1, 0, Format(j, "00"), x
  38.             Tree.Add Node, CStr(i)
  39.             DFSNode Tree, fD, fD(x), i, 1, Format(j, "00") & "01"
  40.         End If
  41.     Next
  42.     Set Father_SonToCollection = Tree
  43. exitLine:
  44.     Set fD = Nothing
  45.     Set sD = Nothing
  46. End Function

  47. Private Function DFSNode(Tree As Collection, fD As Dictionary, _
  48.                         d As Object, nCode&, Level&, strName$)
  49.     Level = Level + 1
  50.     fathercode& = nCode
  51.    
  52.     Dim Node As ClassNode
  53.     Dim fatherNode As ClassNode
  54.     For Each x In d.Keys
  55.         nCode = nCode + 1
  56.             Set Node = New ClassNode
  57.             Node.addDetail nCode, Level, fathercode, strName, x
  58.             Set fatherNode = Tree.Item(CStr(fathercode))
  59.             fatherNode.Children.Add Node
  60.             Tree.Add Node, CStr(nCode)
  61.             
  62.             If fD.Exists(x) Then ''' 不是末级节点,进入下一级节点
  63.                 DFSNode Tree, fD, fD(x), nCode, Level, strName & "01"
  64.                 Level = Level - 1
  65.             End If
  66.             strName = Left(strName, Len(strName) - 2) & _
  67.                     Format(val(Right(strName, 2)) + 1, "00")
  68.     Next
  69. End Function

  70. Function PrintTree(Tree As Collection, rangeTopLeft As Range)
  71.     Dim ar(), x As ClassNode
  72.     n = Tree.Count
  73.     ReDim ar(1 To n, 1 To 20)
  74.         For Each x In Tree
  75.             r = r + 1
  76.             With x
  77.                 br = Array(.Code, .text, .Level, .Parent, .ChildrenCount, "BM" & .Name)
  78.                 For j = 0 To UBound(br)
  79.                     ar(r, j + 1) = br(j)
  80.                 Next
  81.                     ar(r, j + .Level) = .text
  82.             End With
  83.         Next
  84.     rangeTopLeft.CurrentRegion.ClearContents
  85.     rangeTopLeft.Resize(n, UBound(ar, 2)).Value = ar
  86. End Function
复制代码

  1. ''' Class Name : ClassNode

  2. Public Code As Long     ''' 节点编号,在全部节点中唯一
  3. Public Level As Long    ''' 所在层级
  4. Public Name As String   ''' ROOT_节点编号_所在层级_目录编码
  5. Public text As String   ''' 文本值
  6. Public Parent As Long   ''' 父节点的编号
  7. Public Children As Collection
  8. Public Property Get ChildrenCount() As Long
  9.     ChildrenCount = Children.Count
  10. End Property
  11. Public Function addDetail(code0&, level0&, parent0&, name0$, text0)
  12.     Code = code0
  13.     Level = level0
  14.     Parent = parent0
  15.     Name = name0
  16.     text = text0
  17. End Function
  18. Private Sub Class_Initialize()
  19.     Set Children = New Collection
  20. End Sub
复制代码
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-19 00:42 , Processed in 0.042682 second(s), 15 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表