ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[原创] 佛山小老鼠带您走进字典(字典入门帖)

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-11-3 11:21 | 显示全部楼层
本帖已被收录到知识树中,索引项:数组集合和字典
banjinjiu 发表于 2013-11-3 11:13
输入dic.后出现的图标:属性图:方法图:
下次认图标就知道属性和方法了。楼主,对吗?

一个手形那个是属性,绿色的那个方法,绿色的那些有时也会是函数,不过这里的绿色都是方法

TA的精华主题

TA的得分主题

发表于 2013-11-3 11:35 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2013-11-3 12:06 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-11-3 12:11 | 显示全部楼层
骑蚂蚁游江南 发表于 2013-11-3 12:06
字典是VBA的吗

字典不是excel自带的,是外部对象,但是我们可以通过函数CreateObject调用它,来加强,增强excel VBA的功能,让excel里的VBA发挥出更强大的作用

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-11-3 12:15 | 显示全部楼层
佛山小老鼠 发表于 2013-11-3 12:11
字典不是excel自带的,是外部对象,但是我们可以通过函数CreateObject调用它,来加强,增强excel VBA的功能,让 ...

受教了。原来是这样博大精深。

TA的精华主题

TA的得分主题

发表于 2013-11-3 12:26 | 显示全部楼层
本帖最后由 liucqa 于 2013-11-3 12:29 编辑

C#中可以使用自定义类来建立字典的键值和数据项,理解下面的代码,对理解字典的工作原理有重要作用。

初学者不必理会...


  1.     using System;
  2.     using System.Collections.Generic;
  3.     using System.Linq;
  4.     using System.Text;
  5.     namespace 集合
  6.     {
  7.         class 字典
  8.         {
  9.             public static void Main()
  10.             {
  11.                 //字典也称映射或者散列表,主要特点是可以根据键快速查找值,也可以自由删除添加元素
  12.                 //在删除添加时,不会像列表一样,移动之后的所有元素,产生内存的开销。
  13.                 //.net中提供了几个字典,可以使用最主要的类是Dictionary<TKey,TValue>
  14.                 //键的类型  
  15.                 //用做字典中键的类型必须重写Object类中的GetHashCode()方法,只要字典类需要确定元素的位置,就要调用本方法
  16.                 //字典内部通过调用这个方法的返回值,来计算产生散列。。这个算法不做介绍 ,但我要知道,它涉及到素数
  17.                 //所以字典的容量是一个素数
  18.                 //GetHashCode()方法的实现需要遵循以下几点
  19.                 // 1 相同的对象应总是返回相同的值
  20.                 // 2 不同的对象可以返回相同的值
  21.                 // 3 应执行得比较快,计算的开销不大。
  22.                 // 4 不能抛出异常
  23.                 // 5 应至少使用一个实例字段
  24.                 // 6 散列码值应平均分布在int可以存储的整个数字区上
  25.                 // 7 散列码最好在对象的生存期中不发生变化
  26.                 //提示: 字典的性能取决于GetHashCode()方法的实现代码
  27.                
  28.                 Dictionary<EmployeeID , Employee> emps = new Dictionary<EmployeeID,Employee>(31) ;
  29.                 EmployeeID idAladdin = new EmployeeID( "C7102" ) ;
  30.                 Employee aladdin = new Employee( "aladdin" , 5000.00m , idAladdin ) ;
  31.                 emps.Add( idAladdin , aladdin ) ;
  32.                 Console.WriteLine( aladdin ) ;
  33.                 EmployeeID idjacky = new EmployeeID( "C7106" ) ;
  34.                 Employee jacky = new Employee( "jacky" , 5000.00m , idjacky ) ;
  35.                 emps.Add( idjacky , jacky ) ;
  36.                 Console.WriteLine( jacky ) ;
  37.                 EmployeeID idzhao = new EmployeeID( "C8102" ) ;
  38.                 Employee zhao = new Employee( "zhao" , 5000.00m , idzhao ) ;
  39.                 emps.Add( idzhao , zhao ) ;
  40.                 Console.WriteLine( zhao ) ;
  41.                 EmployeeID idxiaofei = new EmployeeID( "C9102" ) ;
  42.                 Employee xiaofei = new Employee( "xiaofei" , 5000.00m , idxiaofei ) ;
  43.                 emps.Add( idxiaofei , xiaofei ) ;
  44.                 Console.WriteLine( xiaofei ) ;
  45.                 EmployeeID iddabi = new EmployeeID( "C7602" ) ;
  46.                 Employee dabi = new Employee( "dabi" , 5000.00m , iddabi ) ;
  47.                 emps.Add( iddabi , dabi ) ;
  48.                 Console.WriteLine( dabi ) ;
  49.                 EmployeeID idalong = new EmployeeID( "C7302" ) ;
  50.                 Employee along = new Employee( "along" , 5000.00m , idalong ) ;
  51.                 emps.Add( idalong , along ) ;
  52.                 Console.WriteLine( along ) ;
  53.                 EmployeeID idcarl = new EmployeeID( "C7402" ) ;
  54.                 Employee carl = new Employee( "carl" , 5000.00m , idcarl ) ;
  55.                 emps.Add( idcarl , carl ) ;
  56.                 Console.WriteLine( carl ) ;
  57.                 EmployeeID idjeff = new EmployeeID( "C7502" ) ;
  58.                 Employee jeff = new Employee( "jeff" , 5000.00m , idjeff ) ;
  59.                 emps.Add( idjeff , jeff ) ;
  60.                 Console.WriteLine( jeff ) ;
  61.                 EmployeeID iddenny = new EmployeeID( "C6602" ) ;
  62.                 Employee denny = new Employee( "denny" , 5000.00m , iddenny ) ;
  63.                 emps.Add( iddenny , denny ) ;
  64.                 Console.WriteLine( denny ) ;
  65.                 EmployeeID idmatt = new EmployeeID( "C7701" ) ;
  66.                 Employee matt = new Employee( "matt" , 5000.00m , idmatt ) ;
  67.                 emps.Add( idmatt , matt ) ;
  68.                 Console.WriteLine( matt ) ;
  69.                 Console.ReadLine() ;
  70.             }
  71.         }
  72.         struct EmployeeID : IEquatable<EmployeeID>
  73.         {
  74.             private readonly char prefix ;
  75.             private readonly int number ;
  76.             public EmployeeID( string id )
  77.             {
  78.                 this.prefix = (id.ToUpper())[0] ;
  79.                 int numLength = id.Length - 1 ;
  80.                 this.number = int.Parse( id.Substring( 1 , numLength > 6 ? 6 : numLength ) ) ;
  81.             }
  82.             public override string ToString()
  83.             {
  84.                 return this.prefix.ToString() + string.Format( "{0,6:000000}" , number ) ;
  85.             }
  86.             public override int GetHashCode()
  87.             {
  88.                 return ( number ^ number << 16 ) * 0x15051505 ;
  89.             }
  90.             public bool Equals( EmployeeID other )
  91.             {
  92.                 return (  other.number == this.number && this.prefix == other.prefix  ) ;
  93.             }
  94.         }
  95.         class Employee
  96.         {
  97.             private string name ;
  98.             private decimal salary ;
  99.             private readonly EmployeeID id ;
  100.             public Employee( string name , decimal salary , EmployeeID id )
  101.             {
  102.                 this.name = name ;
  103.                 this.salary = salary ;
  104.                 this.id = id ;
  105.             }
  106.             public override string ToString()
  107.             {
  108.                 return string.Format( "{0} : {1,-20} {2:C}" , id.ToString() , name,salary ) ;
  109.             }
  110.         }
  111.     }

复制代码

TA的精华主题

TA的得分主题

发表于 2013-11-3 12:34 | 显示全部楼层
本帖最后由 liucqa 于 2013-11-4 11:27 编辑

其实,字典就是一个特殊的哈希表,所以在C#中,完全可以用哈希表来替代字典,因此,下面的介绍也可以帮助你理解字典

C#:字典的工作原理

    在C#中,我们可能经常用到使用非常方便的Hashtable,不知大家是否知道它的另外一个名字:散列表.事实上Hashtable使用了某种算法,通过键(key)来确定每个对象的位置,实际上,该算法并不完全是Hashtable类提供的.它有两个部分,其中的一部分的代码是有key类来完成.我们平常在使用Hashtable的时候,key我们一般使用string类(部分算法string已经提供,Microsoft已经替我们做了),所以不会有任何的问题,但是如果key类是用户自己编写的,就必须自己编写这部分算法了.

    下面我们用EmployeeID类作为key类,来讲解如何编写自己的算法.

     在计算机中.由key类执行的部分算法成为散列,Hashtable在散列算法中有特殊的地位,它提供了对象的GetHashCode()方法,该方法继承于Sysetm.Object.只要字典需要确定数据项的位置,就会条用key对象的GetHashCode()方法.所以我们如果要是我们的key类起作用的话,就必须重写GetHashCode()方法.

      GetHashCode()方法的工作方式是:返回一个int型的数据,它使用这个键的值来生成int类型的数据.Hashtable获取这个值,并对它进行其他的一些操作,其中涉及一些非常复杂的计算,最后返回一个索引,表示带有指定散列的数据项在字典中的位置.这部分算法呢据说很复杂,非我辈能力所及,就不介绍了.免得招来拍砖无数.

     重载GetHashCode()方法就可以了吗?答案是否顶的!比如:如果再字典中,两个数据项的散列有相同的索引,该怎么办?所以要确保字典的容量大于其中元素的个数.而且,如果两个对象包含相同的数据,他们就必须给出相同的散列值.所以重写System.Object的Equals()和GetHashCode()方法时,必须考虑这个.

    下面我们通过具体的例子进行讲解:

     比如A公司建立了一个员工字典,该字典用EmployeeID对象做索引,存储的数据都是一个EmployeeData对象(员工的详细数据).我们的实例创建一个字典,添加了两个员工,通过员工的id,获取相应的信息.

     首先我们看下我EmployeeID类和EmployeeData类. EmployeeID类是关键,散列的部分算法就在EmployeeID类中实现.   

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;

  5. /// <summary>
  6. /// Summary description for EmployeeID
  7. /// </summary>
  8. public class EmployeeID
  9. {
  10.     public EmployeeID()
  11.     {
  12.         //
  13.         // TODO: Add constructor logic here
  14.         //
  15.     }
  16.     public EmployeeID(string id)
  17.     {
  18.         Id = id;        
  19.     }
  20.     private string Id { get; set; }

  21.     //
  22.     public override string ToString()
  23.     {
  24.         return base.ToString();
  25.     }
  26.     //必须重写,否则EmployeeID类,不能作为key类,
  27.     public override int GetHashCode()
  28.     {
  29.         return ToString().GetHashCode();
  30.     }
  31.     //重载Equals方法,确保两个相同的对象,返回相同的散列值
  32.     public override bool Equals(object obj)
  33.     {
  34.         EmployeeID employeeID = obj as EmployeeID;
  35.         if (employeeID == null) return false;
  36.         if (this.Id == employeeID.Id) return true;
  37.         return false;
  38.     }
  39. }
复制代码

   上面的代码 ,我们重点看下GetHashCode()方法,我们前面讲了,如果要实现散列算法,必须满足很多苛刻的要求.但是我们知道string类可以直接作为Hashtable的key类,说明Microsoft已经为string类提供了很有效的散列算法.所以我们直接应用string.GetHashCode()去重写我们的EmployeeID类的GetHashCode()方法.当然性能可能会有损失,因为EmployeeID类转换为string的时候会损失性能.

    EmployeeData类就没有什么特别的啦,就一些数据   
  1. EmployeeData类

  2. Main 测试代码


  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Collections;

  7. namespace HashTableTest
  8. {
  9.     class Program
  10.     {
  11.         static void Main(string[] args)
  12.         {
  13.             Hashtable ht = new Hashtable();
  14.             ht.Add(new EmployeeID("A0001"), new EmployeeData("Johnny", "24", "Man"));
  15.             ht.Add(new EmployeeID("A0002"), new EmployeeData("Vicky", "20", "female"));
  16.             ht.Add(new EmployeeID("A0003"), new EmployeeData("Allen", "30", "male"));
  17.             Console.WriteLine("Please input the Employee ID");
  18.             string userId = Console.ReadLine();
  19.             //当输入ID的是否,返回员工的详细数据
  20.             EmployeeData E1 = (EmployeeData)ht[new EmployeeID(userId)];
  21.             if (E1 != null)
  22.             {
  23.                 Console.WriteLine("Employee " + userId+"Data is :");
  24.                 Console.WriteLine(E1.Name);
  25.                 Console.WriteLine(E1.Age);
  26.                 Console.WriteLine(E1.Gender);
  27.             }
  28.             Console.ReadLine();
  29.         }
  30.     }
  31. }
复制代码

关于字典的哈希算法,可以看看微软的这个代码
http://support.microsoft.com/kb/71891/en

在VBA中使用类模块实现字典的一对多
http://club.excelhome.net/thread-1068628-1-2.html

TA的精华主题

TA的得分主题

发表于 2013-11-3 14:57 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2013-11-3 15:39 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-11-3 16:47 | 显示全部楼层
本帖最后由 佛山小老鼠 于 2013-11-3 16:50 编辑

第一个案例:
1.多行2列分类汇总
2.多行多列分类汇总

  1. Option Explicit
  2. Sub 二列多行()
  3.     Dim arr1, dic, x, arr2(1 To 10, 1 To 2), m%, k% '定义变量
  4.     Set dic = CreateObject("Scripting.dictionary") '后期绑定引用字典
  5.     arr1 = Range("A1").CurrentRegion '把单元区域装到数组arr1
  6.     For x = 2 To UBound(arr1, 1) '循环数组arr1的行
  7.        If dic.exists(arr1(x, 1)) Then '判断数组元素arr1(x,1)在字典关键词里是否存在,
  8.            m = dic(arr1(x, 1)) '如果存在,把关键词arr1(x,1)的条目读出来,在原来的
  9.        '基础上累加,通过读取关键词arr1(x,1)的条目,找到在数组arr2那一行上累加
  10.            arr2(m, 2) = arr2(m, 2) + arr1(x, 2) '在数组arr2第m行,第2列上累加
  11.        Else '如果关键词arr1(x,1)不存在,那么
  12.             k = k + 1 '计数
  13.             dic(arr1(x, 1)) = k '把数组arr1(x,1)装到字典dic里,条目装一个k,
  14.             '这个k的作用来给数组arr2中找到存放那一行
  15.             arr2(k, 1) = arr1(x, 1) '把数组arr1里的第x行第1列装到数组arr2的第k行,第1列
  16.             arr2(k, 2) = arr1(x, 2) '把数组arr1里的第x行第2列装到数组arr2的第k行,第2列
  17.        End If
  18.     Next x
  19.     Range("E1:F" & Rows.Count) = "" '清空区域,用来存放新的数据
  20.     [E1:F1] = Array("产品名称", "数量") '填充表头
  21.     [E2].Resize(k, 2) = arr2 '把数组arr2读到单元格区域
  22. End Sub

  23. Sub 多列多行汇总()
  24.     Dim dic, arr1, x%, MySt, k%, arr2(1 To 15, 1 To 3), y%, m%
  25.     Set dic = CreateObject("Scripting.dictionary")
  26.     arr1 = Range("A1").CurrentRegion
  27.     For x = 2 To UBound(arr1, 1)
  28.         MySt = arr1(x, 1) & arr1(x, 2)
  29.         If dic.exists(MySt) Then
  30.            m = dic(MySt)
  31.            arr2(m, 3) = arr2(m, 3) + arr1(x, 3)
  32.         Else
  33.             k = k + 1
  34.             dic(MySt) = k
  35.             For y = 1 To 3
  36.                 arr2(k, y) = arr1(x, y)
  37.             Next y
  38.         End If
  39.     Next x
  40.     Range("E1:G" & Rows.Count) = ""
  41.     [E1:G1] = Array("产品名称", "款号", "数量")
  42.     [E2].Resize(k, 3) = arr2
  43. End Sub

  44. Sub 清空1()
  45.     Range("E1:F" & Rows.Count) = ""
  46. End Sub
  47.   
  48. Sub 清空2()
  49.     Range("E1:G" & Rows.Count) = ""
  50. End Sub
复制代码
第二个代码我就不加注解了,同第一个代码差不多,区别是
由于关键字只能装1列,如果有多列怎么办呢?
我们可以把多列用&串起来,多串字符串就变成了一串字符串
附件在第1楼

评分

1

查看全部评分

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-12-22 11:34 , Processed in 0.037507 second(s), 7 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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