Lua实现的hash合并功能

问题背景

业务需求要通过事件触发, 来实现两个 table 的数据合并.

理清了需求和思路并思考下扩展, 记录下来, 用于分享和沉淀.

因为用 lua 写的业务, 所以这里用 lua 实现的.

PS: 在 lua 里 table 和 hash 的结构类似

一层合并

数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local a = {
["a"] = {
["aa"] = {
["aaa"] = "111"
},
["bb"] = {
["bbb"] = "222"
}
}
}

local b = {
["a"] = {
["aa"] = {
["aaa"] = "123123"
}
}
}

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function table.merge(dest, src, cover)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

-- don't use 'if not cover then'
-- because "(not nil or not false) == true"
-- T.T
if cover == nil then
cover = true
end

for k, v in pairs(src) do
if dest[k] then
if cover then
dest[k] = v
end
else
dest[k] = v
end
end
end

结果

1
2
3
4
5
6
7
{
["a"] = {
["aa"] = {
["aaa"] = "123123",
},
},
}

你看我想将 table B.a.aa.aaa 的值合并到 table A.a.aa.aaa

但是直接用 table B.a 将 table A.a 的值给覆盖了

所以这个函数只能用于一层 hash 函数的合并了

递归合并

数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
local a = {
["a"] = {
["aa"] = {
["aaa"] = "111"
}
},
["b"] = {
["bb"] = {
["bbb"] = "222"
}
},
["c"] = {
["cc"] = {
["ccc"] = "333"
}
}
}

local b = {
["a"] = {
["aa"] = {
["aaa"] = "111a"
["aaa1"] = "111a"
}
},
["b"] = {
["bb"] = 123123
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
}
}
}
}

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function table.r_merge(dest, src, cover)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

if cover == nil then
cover = true
end

for k, v in pairs(src) do
if type(v) == "table" then
if dest[k] and type(dest[k]) == "table" then
table.r_merge(dest[k], v, cover)
elseif cover then
dest[k] = v
end
else
if cover then
dest[k] = v
end
end
end
end

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
["a"] = {
["aa"] = {
["aaa1"] = "111a",
["aaa"] = "111a",
},
},
["b"] = {
["bb"] = 123123,
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
},
},
},
}

table B.a.aa.aaa1 追加到了 table A.a.aa 里面

table B.a.aa.aaa 覆盖了 table A.a.aa.aaa

table B.b.bb 覆盖了 table A.b.aa

table B.c.cc.ccc 覆盖了 table A.c.cc.ccc

层级合并

扩展思考

一层合并和递归合并都不灵活

所以需要有一个提供给调用者想要合并层级的函数

数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
local A = {
["a"] = {
["aa"] = {
["xxx"] = "111"
},
["bb"] = {
["bbb"] = "222"
}
},
["b"] = {
["bb"] = {
["bbb"] = "222"
}
},
["c"] = {
["cc"] = {
["ccc"] = "333"
}
}
}

local B = {
["a"] = {
["aa"] = {
["aaa"] = "111",
["aaa1"] = "111a",
["aaaa"] = "1111",
["aaaaa"] = "11111",
},
["cc"] = "333",
},
["b"] = {
["bb"] = 123123
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
}
}
}
}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function table.r_merge(dest, src, cover, depth)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

if type(depth) == "number" then
depth = depth - 1
elseif depth == nil then
-- 这里有点意思, 当depth == -1
-- 再次递归的时候永远都是负数.
-- 根据下面 depth == 0 的判断.
-- 就形成了无限递归的条件
depth = -1
else
return
end

if cover == nil then
cover = true
end

for k, v in pairs(src) do
if type(v) == "table" then
if cover and depth == 0 then
dest[k] = v
elseif dest[k] and type(dest[k]) == "table" then
table.r_merge(dest[k], v, cover, depth)
elseif dest[k] and cover then
dest[k] = v
end
else
if cover then
dest[k] = v
end
end
end
end

其实我不喜欢这样的函数.

因为在调用的时候就要明确的知道层级的范围是多少.

也就是说层级前和层级后的数据是不同的.

这种结构的不同是隐性存在的.

需要调用者自行把控.

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
["a"] = {
["bb"] = {
["bbb"] = "222",
},
["cc"] = "333",
["aa"] = {
["aaa1"] = "111a",
["aaaaa"] = "11111",
["aaa"] = "111",
["aaaa"] = "1111",
},
},
["b"] = {
["bb"] = 123123,
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
},
},
},
}

table A.a.bb 添加到了 table A.a

table B.aa 覆盖了 table A.a.aa, A.a.aa.xxx 没有了, 去哪了? ^.^

table A.a.bb 还在 A 里乖乖的呆着.

table B.b.bb 覆盖了 table A.b.bb

table B.c.cc 覆盖了 table A.c.cc