ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[讨论] 字典怎么玩?A列B列数据比较

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2015-10-8 11:07 | 显示全部楼层 |阅读模式
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
看到1个有趣的题目:使用字典对A列B列数据进行比较

要求输出4种不重复统计结果:
1. A列有B列没有 即A1B0
2. B列有A列没有 即A0B1
3. A列有B列也有 即A1B1
4. A列有或B列有 即A+B (A or B)

请看参考附件。



AB.zip

14.55 KB, 下载次数: 720

评分

3

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-10-8 11:09 | 显示全部楼层
代码及注释。
  1. Option Base 1
  2. Sub test()
  3.     Dim ar, dic, i&, k1&, k2&, k3&, k4&, m&, t$, tr, tms#
  4.     tms = Timer
  5.    
  6.     [d1].CurrentRegion.Offset(1) = "" '清空输出区域
  7.    
  8.     m1 = [a65536].End(3).Row - 1 '获取A列最大行数m1
  9.     ar = [a2].Resize(m1) '读取A列数据到数组ar

  10.     m2 = [b65536].End(3).Row - 1 '获取B列最大行数m2
  11.     ReDim br1(m1, 1), br2(m2, 1), br3(m1, 1), br4(m1 + m2, 1)
  12.     '建立存放结果的数组br1、br2、br3、br4 (不超过可能个数)

  13.     Set dic = CreateObject("Scripting.Dictionary") '建立字典dic
  14.     For i = 1 To m1 '遍历A列数据
  15.         t = ar(i, 1): If Len(t) Then If Not dic.Exists(t) Then dic(t) = t: k4 = k4 + 1: br4(k4, 1) = t
  16.         '如不为空则检查是否已经存入字典、并将第1次结果存入br4 即【A+B合成】(A or B)
  17.     Next
  18.    
  19.     ar = [b2].Resize(m2) '读取B列数据到数组ar (数组ar重复使用)
  20.     For i = 1 To m2 '遍历B列数据
  21.         t = ar(i, 1)
  22.         If Len(t) Then '如不为空
  23.             If dic.Exists(t) Then '如字典存在 则A1B1 即AB都有
  24.                 If Len(dic(t)) Then dic(t) = "": k3 = k3 + 1: br3(k3, 1) = t
  25.                 '如字典Item结果不为空则属于第1次出现,标记Item为空 并记入br3【AB都有】
  26.                 '如Item为空 则为标记已重复 不用统计【重要技巧】
  27.             Else '如字典不存在 A0B1 即B有A没有
  28.                 k2 = k2 + 1: br2(k2, 1) = t '记入br2【B有A没有】
  29.                 k4 = k4 + 1: br4(k4, 1) = t '记入br4【A+B合成】(A or B)
  30.                 dic(t) = "" '该值Item标记为空 防止重复统计【重要技巧】
  31.             End If
  32.         End If
  33.     Next
  34.     '以上检查完毕,但A有B没有结果只存在于字典中,还需要检查输出

  35.     tr = dic.items '提取字典中结果(A有B有时为空、B有时为空、仅A有B没有才是结果)
  36.     For i = 0 To UBound(tr) '遍历字典结果
  37.         t = tr(i): If Len(t) Then k1 = k1 + 1: br1(k1, 1) = t
  38.         '如果Item不为空才是仅A有B没有的正确结果 输出到br1
  39.     Next
  40.      
  41.     '以下为输出   
  42.     [d2].Resize(k1) = br1
  43.     [e2].Resize(k2) = br2
  44.     [f2].Resize(k3) = br3
  45.     [g2].Resize(k4) = br4
  46.     MsgBox Format(Timer - tms, "0.00s")
  47. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-10-8 11:33 | 显示全部楼层
再换一种算法:
使用1个字典、A列B列各一次循环,最后对字典结果再进行一次循环检查以便输出结果:

  1. Sub test2()
  2.     Dim ar, kr, tr, dic, i&, k1&, k2&, k3&, k4&, m&, r&, t$, tms#
  3.     tms = Timer
  4.    
  5.     [d1].CurrentRegion.Offset(1) = ""
  6.    
  7.     m = [a65536].End(3).Row - 1
  8.     ar = [a2].Resize(m)
  9.    
  10.     Set dic = CreateObject("Scripting.Dictionary")
  11.     For i = 1 To m
  12.         t = ar(i, 1): If Len(t) Then dic(t) = 1 'A列有标记=1
  13.     Next
  14.    
  15.     m = [b65536].End(3).Row - 1
  16.     ar = [b2].Resize(m)
  17.     For i = 1 To m
  18.         t = ar(i, 1)
  19.         If Len(t) Then
  20.             If Not dic.Exists(t) Then 'A0B1
  21.                 dic(t) = 2 'A没有B有标记为=2
  22.             Else
  23.                 If dic(t) = 1 Then dic(t) = 3 '标记=1时则为A有 且B有 改标记=3
  24.             End If
  25.         End If
  26.     Next
  27.    
  28.     kr = dic.keys
  29.     tr = dic.items
  30.     ReDim br(1 To dic.Count, 1 To 4)
  31.     For i = 0 To dic.Count - 1
  32.         r = tr(i): t = kr(i)
  33.         If r = 1 Then k1 = k1 + 1: br(k1, 1) = t '标记=1时仅A有B没有
  34.         If r = 2 Then k2 = k2 + 1: br(k2, 2) = t '标记=2时仅B有A没有
  35.         If r = 3 Then k3 = k3 + 1: br(k3, 3) = t '标记=3时A有B也有
  36.         k4 = k4 + 1: br(k4, 4) = t '字典中留下的就是A+B合成(A有 or B有)
  37.     Next
  38.    
  39.     MsgBox Format(Timer - tms, "0.00s")
  40.     [d2].Resize(dic.Count, 4) = br
  41. End Sub
复制代码


点评

第二和第二个循环可以合并掉,字典只记录行号即可  发表于 2015-11-24 08:57

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-10-8 12:26 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
参与一下:
Sub test1()
Dim d, d1, r&, i&, ar, br(1 To 65536, 1 To 4), n1&, n2&, n3&, n4&, dk, t
t = Timer
Set d = CreateObject("scripting.dictionary")
Set d1 = CreateObject("scripting.dictionary")
r = [a65536].End(3).Row
i = [b65536].End(3).Row
If r < i Then r = i
ar = Range("a2:b" & r)
For i = 1 To r - 1
    If ar(i, 1) <> "" Then d(ar(i, 1)) = ""
    If ar(i, 2) <> "" Then d1(ar(i, 2)) = ""
Next i
dk = d.keys
For i = 0 To UBound(dk)
    n4 = n4 + 1
    br(n4, 4) = dk(i)
    If d1.exists(dk(i)) = 0 Then
        n1 = n1 + 1
        br(n1, 1) = dk(i)
    Else
        n3 = n3 + 1
        br(n3, 3) = dk(i)
    End If
Next i
dk = d1.keys
For i = 0 To UBound(dk)
    If d.exists(dk(i)) = 0 Then
        n4 = n4 + 1
        br(n4, 4) = dk(i)
        n2 = n2 + 1
        br(n2, 2) = dk(i)
    End If
Next i
[r2].Resize(n4, 4) = br
MsgBox Timer - t
End Sub

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-10-8 14:12 | 显示全部楼层
学习香川群子的技巧:在Item上耍花枪。无需remove

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-10-8 15:37 | 显示全部楼层
如果使用4个字典分别存放4种不同类型结果,那么代码如下:
显然这么做速度效率比较低。但逻辑比较容易懂。

  1. Sub test4()
  2.     Dim ar, dic1, dic2, dic3, dic4, i&, m&, t$, tms#
  3.     tms = Timer
  4.    
  5.     m = [a65536].End(3).Row - 1
  6.     ar = [a2].Resize(m)
  7.    
  8.     Set dic1 = CreateObject("Scripting.Dictionary") 'A1B0
  9.     Set dic2 = CreateObject("Scripting.Dictionary") 'A0B1
  10.     Set dic3 = CreateObject("Scripting.Dictionary") 'A1B1
  11.     Set dic4 = CreateObject("Scripting.Dictionary") 'A+B
  12.     For i = 1 To m
  13.         t = ar(i, 1): If Len(t) Then dic1(t) = "": dic4(t) = ""
  14.     Next
  15.    
  16.     m = [b65536].End(3).Row - 1
  17.     ar = [b2].Resize(m)
  18.    
  19.     For i = 1 To m
  20.         t = ar(i, 1)
  21.         If Len(t) Then
  22.             dic4(t) = "" 'A+B
  23.             If Not dic3.exists(t) Then
  24.                 If dic1.exists(t) Then 'A1B1
  25.                     dic1.Remove t
  26.                     dic3(t) = "" 'A1B1
  27.                 Else
  28.                     dic2(t) = "" 'A0B1
  29.                 End If
  30.             End If
  31.         End If
  32.     Next
  33.    
  34.     MsgBox Format(Timer - tms, "0.00s")
  35.     [d1].CurrentRegion.Offset(1) = ""
  36.     [d2].Resize(dic1.Count) = WorksheetFunction.Transpose(dic1.keys)
  37.     [e2].Resize(dic2.Count) = WorksheetFunction.Transpose(dic2.keys)
  38.     [f2].Resize(dic3.Count) = WorksheetFunction.Transpose(dic3.keys)
  39.     [g2].Resize(dic4.Count) = WorksheetFunction.Transpose(dic4.keys)
  40. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-10-8 15:39 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 香川群子 于 2015-10-8 23:24 编辑

4楼小花鹿的是双字典法,需要对2个字典各自检查结果。

改写如下:
  1. Sub test3()
  2.     Dim ar, kr, dic1, dic2, i&, k1&, k2&, k3&, k4&, m&, t$, tms#
  3.     tms = Timer
  4.         
  5.     m = [a65536].End(3).Row - 1
  6.     ar = [a2].Resize(m)
  7.     Set dic1 = CreateObject("Scripting.Dictionary") 'A
  8.     For i = 1 To m
  9.         t = ar(i, 1): If Len(t) Then dic1(t) = ""
  10.     Next
  11.    
  12.     m = [b65536].End(3).Row - 1
  13.     ar = [b2].Resize(m)
  14.     Set dic2 = CreateObject("Scripting.Dictionary") 'B
  15.     For i = 1 To m
  16.         t = ar(i, 1): If Len(t) Then dic2(t) = ""
  17.     Next
  18.    
  19.     ReDim br(1 To dic1.Count + dic2.Count, 1 To 4)
  20.     kr = dic1.keys
  21.     For i = 0 To UBound(kr)
  22.         t = kr(i)
  23.         If Not dic2.exists(t) Then
  24.             k1 = k1 + 1: br(k1, 1) = t
  25.         Else
  26.             k3 = k3 + 1: br(k3, 3) = t
  27.         End If
  28.         k4 = k4 + 1: br(k4, 4) = t
  29.     Next
  30.    
  31.     kr = dic2.keys
  32.     For i = 0 To UBound(kr)
  33.         t = kr(i)
  34.         If Not dic1.exists(t) Then
  35.             k2 = k2 + 1: br(k2, 2) = t
  36.             k4 = k4 + 1: br(k4, 4) = t
  37.         End If
  38.     Next
  39.    
  40. '    Exit Sub
  41.     MsgBox Format(Timer - tms, "0.00s")
  42.     [d1].CurrentRegion.Offset(1) = ""
  43.     [d2].Resize(k4, 4) = br
  44. End Sub
复制代码


A+B输出错误已更正

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-10-9 10:39 | 显示全部楼层
小花鹿 发表于 2015-10-8 12:26
参与一下:
Sub test1()
Dim d, d1, r&, i&, ar, br(1 To 65536, 1 To 4), n1&, n2&, n3&, n4&, dk, t

在什么情况下用两个字典?

TA的精华主题

TA的得分主题

发表于 2015-10-9 12:56 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2015-10-10 21:28 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
加深理解,灵活应用字典
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-11-16 15:36 , Processed in 0.055561 second(s), 17 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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