如何将路径数据转换为邻接表

时间:2019-06-11 03:20:58

标签: mysql ruby graph hierarchical-data adjacency-list

我正在设置Rails“从csv导入”任务,并且遇到了路径形式的部门数据(以db为单位)。我希望它成为邻接表。

我有……

ID, NAME, PATH
---------
1,Valve,000
2,Steam,000.000
3,Sales,000.000.000
4,Developers,000.000.112
7,Designers,000.000.112.000
8,Game Designers,000.000.112.000.000
9,UI Designers,000.000.112.000.002
10,Web Designers,000.000.112.000.001
11,3D Designers,000.000.112.000.003
12,Accounting managers,000.000.114.000
13,Accounting topmanagers,000.000.114.000.000

我想要的东西:

ID, NAME, PATH, PARENT_ID
---------
1,Valve,000, nil
2,Steam,000.000, 1
3,Sales,000.000.000, 2
4,Developers,000.000.112, 2
7,Designers,000.000.112.000, 4
8,Game Designers,000.000.112.000.000, 7
9,UI Designers,000.000.112.000.002, 7
10,Web Designers,000.000.112.000.001, 7
11,3D Designers,000.000.112.000.003, 7
12,Accounting managers,000.000.114.000, 322
13,Accounting topmanagers,000.000.114.000.000, 12

1 个答案:

答案 0 :(得分:1)

该字符串似乎描述了有向树,但会计经理除外,

'12,Accounting managers,000.000.114.000'

似乎没有老板。因此,我添加了

'14,Accounting big cheese,000.000.114'

这是数据。

data =<<-_
ID, NAME, PATH
---------
1,Valve,000
2,Steam,000.000
3,Sales,000.000.000
4,Developers,000.000.112
7,Designers,000.000.112.000
8,Game Designers,000.000.112.000.000
9,UI Designers,000.000.112.000.002
10,Web Designers,000.000.112.000.001
11,3D Designers,000.000.112.000.003
14,Accounting big cheese,000.000.114
12,Accounting managers,000.000.114.000
13,Accounting topmanagers,000.000.114.000.000
_

我们可以使用split("\n")将此字符串转换为行数组,然后按以下方法确定每个节点的亲子关系。

r1, r2, *rest = data.split("\n")
str = [
  r1,
  r2,
  rest.map do |s|
    parent_match = s[/(?:\d{3}\.)*\d{3}(?=\.\d{3})/]
    parent = arr.find { |ss| parent_match == ss[/(?:\d{3}\.)*\d{3}/] }
    parent.nil? ? "#{s}, nil" : "#{s}, #{ parent[/\d+/] }" 
    end
].join("\n")

puts str 
ID, NAME, PATH
---------
1,Valve,000, nil
2,Steam,000.000, 1
3,Sales,000.000.000, 2
4,Developers,000.000.112, 2
7,Designers,000.000.112.000, 4
8,Game Designers,000.000.112.000.000, 7
9,UI Designers,000.000.112.000.002, 7
10,Web Designers,000.000.112.000.001, 7
11,3D Designers,000.000.112.000.003, 7
14,Accounting big cheese,000.000.114, 2
12,Accounting managers,000.000.114.000, 14
13,Accounting topmanagers,000.000.114.000.000, 12

map的块中假设

s = '8,Game Designers,000.000.112.000.000'

然后

parent_match = s[/(?:\d{3}\.)*\d{3}(?=\.\d{3})/]
  #=> "000.000.112.000" 

parent_match是由s中的句点分隔的所有三进制数字组成的字符串,除了最后一个句号后跟最后一个三进制数字。正则表达式为:“匹配零个或多个3位数字组,后跟一个句点,再匹配3位数字,条件是此匹配后紧跟一个句点和3位数字((?=\.\d{3})),它们是正向超前)。

然后我们遍历rest,寻找以parent_match结尾的元素:

parent = rest.find { |ss| parent_match == ss[/(?:\d{3}\.)*\d{3}/] }
  #=> "7,Designers,000.000.112.000"

正则表达式/(?:\d{3}\.)*\d{3}/的内容为:“匹配零个或多个3位数字的组,后跟一个句点,再跟3位数字”。

在下一行:

parent.nil?
  #=> false

因此该区块返回

"#{s}, #{ parent[/\d+/] }" 
  #=> "8,Game Designers,000.000.112.000.000, 7"

parent[/\d+/]仅提取parent开头的数字字符。

我没有添加该行

14,Accounting big cheese,000.000.114

下面的行('12,Accounting ...')已结束,', nil'