|
HI,大家好,我是星光。在Excel基礎(chǔ)教程里,咱們講過(guò)函數(shù)+透視表是普通表格用戶處理數(shù)據(jù)的最佳組合,函數(shù)用于數(shù)據(jù)清洗整理,透視表用于數(shù)據(jù)匯總分析。如果你接觸過(guò)Power BI,會(huì)發(fā)現(xiàn)類似的情況,PowerQuery用于數(shù)據(jù)清洗整理,PowerPivot用于數(shù)據(jù)匯總分析。而VBA編程呢?也不例外,數(shù)組用于數(shù)據(jù)清洗整理,字典用于數(shù)據(jù)匯總分析……關(guān)于數(shù)組,可以看「零基礎(chǔ)學(xué)VBA編程系列教程」中的數(shù)組四篇,關(guān)于字典——打個(gè)響指,今天就來(lái)給大家講下什么是字典。 前方高能預(yù)警,本文共分8節(jié),建議先收后看。 目錄如下▼ 1 什么是字典 2 如何創(chuàng)建字典 3 如何將數(shù)據(jù)存入字典 4 如何將數(shù)據(jù)從字典取出 5 如何移除字典元素 6 如何遍歷字典元素 7 如何釋放字典 8 思考和其它 …… 1 丨 什么是字典 談到字典,有些朋友可能會(huì)想到新華字典、康熙字典、英漢字典、有道字典等等。我今天說(shuō)的字典,和這些家伙——都沒(méi)關(guān)系。字典(Dictionary)是VBA編程中的一個(gè)對(duì)象,具有操作簡(jiǎn)單、運(yùn)行高效的特點(diǎn),常用于數(shù)據(jù)的條件查詢、聚合匯總等。如果說(shuō)數(shù)組是VBA處理數(shù)據(jù)的基礎(chǔ)結(jié)構(gòu),那么字典就可以被稱為核心。攤手,騙你娶你,不論男女。 我舉個(gè)例子。 如上圖所示的數(shù)據(jù)表,如果我們需要查詢看見(jiàn)星光的特長(zhǎng),數(shù)組的解法步驟如下▼ 首先將數(shù)據(jù)源存入數(shù)組,然后遍歷姓名,第1個(gè)人名不是,第2個(gè)人名不是,第3個(gè)人名不是,第4個(gè)還不是……一直到第7個(gè)人名才是,于是取特長(zhǎng),退出數(shù)組循環(huán)…… 數(shù)組循環(huán)是線性查找的方式,只能一個(gè)個(gè)元素找過(guò)去,如果查找值過(guò)多,效率就很不理想。 如果是用字典呢? 首先將數(shù)據(jù)源裝入字典,然后用一句代碼就可以查詢相關(guān)人名的特長(zhǎng)了。 比如查詢看見(jiàn)星光的特長(zhǎng)..▼ 字典('看見(jiàn)星光')不用懷疑,不用循環(huán),就是這么簡(jiǎn)單。 …… 還記得嗎?在數(shù)組四篇之什么是數(shù)組里,咱們講過(guò)數(shù)組處理數(shù)據(jù)的過(guò)程可以分為5步。創(chuàng)建數(shù)組->將數(shù)據(jù)存入數(shù)組->數(shù)組運(yùn)算->將數(shù)組數(shù)據(jù)寫入Excel->釋放數(shù)組。 字典處理數(shù)據(jù)的過(guò)程與數(shù)組十分相似:創(chuàng)建字典->將數(shù)據(jù)裝入字典->將數(shù)據(jù)從字典取出->釋放字典。 ——那么如何創(chuàng)建字典? 2 丨 如何創(chuàng)建字典 如上文所講,字典(Dictionary)是VBA編程的一個(gè)對(duì)象,但它并非VBA自帶的妻妾,而是集成在動(dòng)態(tài)鏈接庫(kù)文件Scrrun.dll中,需要綁定文件后才能夠使用。綁定文件有兩種方式,前期綁定和后期綁定。 先來(lái)說(shuō)前期綁定。 操作步驟如下▼ 在VBE編輯器的[工具]選項(xiàng)卡下,單擊[引用],在打開(kāi)的[引用]對(duì)話框中勾選'Microsoft Scripting Runtim'選項(xiàng),單擊[確定]按鈕,關(guān)閉對(duì)話框即可。 通過(guò)'前期綁定'的方式引用Scrrun.dll文件后,即可在VBA代碼中利用Dim語(yǔ)句聲明變量為字典對(duì)象,然后使用字典處理數(shù)據(jù)。 示例代碼如下..▼
第2句代碼聲明一個(gè)字典對(duì)象,其名為d。語(yǔ)法格式如下▼ Dim 變量名 As New Dictionary…… 然后再說(shuō)下后期綁定。 所謂后期綁定是指通過(guò)CreateObject函數(shù)創(chuàng)建對(duì)字典對(duì)象的引用,示例代碼如下▼
但是——前期綁定的代碼不適合發(fā)送給其它用戶使用,畢竟其它用戶未必會(huì)去手動(dòng)綁定字典對(duì)象;因此后期綁定的方式兼容性更強(qiáng)些。 3 丨 如何將數(shù)據(jù)裝入字典 從表格角度,通俗而言,字典是有兩個(gè)一維數(shù)組或者說(shuō)兩列數(shù)據(jù)構(gòu)成的特殊表。第1列是關(guān)鍵字,被稱為Key;第2列是每個(gè)關(guān)鍵字對(duì)應(yīng)的條目,被稱為Item。其中每個(gè)關(guān)鍵字在字典中都是唯一的,不會(huì)也不允許出現(xiàn)重復(fù)值。
那么如何將數(shù)據(jù)裝入字典呢? 直接賦值就可以了。語(yǔ)法格式如下▼ 字典(關(guān)鍵字)=條目舉個(gè)例子。
第2行代碼使用前期綁定的方式聲明一個(gè)字典對(duì)象,其名為d。 第3行代碼將關(guān)鍵字'看見(jiàn)星光'裝入字典,對(duì)應(yīng)的條目是99。此時(shí)字典有一行數(shù)據(jù),如下圖所示。 第4行代碼將關(guān)鍵字'Excel星球'裝入字典,對(duì)應(yīng)的條目是98。此時(shí)字典就會(huì)有兩行數(shù)據(jù),如下圖所示。 但是我們前面講過(guò),字典的關(guān)鍵字必須是唯一的,如果字典中已經(jīng)存在了某個(gè)關(guān)鍵字,我們又往里面添加了同樣的關(guān)鍵字,會(huì)怎么樣呢? 舉個(gè)例子。 Sub 字典添加重復(fù)數(shù)據(jù)() Dim d As New Dictionary '聲明一個(gè)字典對(duì)象 d('看見(jiàn)星光') = 99 d('看見(jiàn)星光') = 59End Sub如果我不想字典更新已經(jīng)存在的關(guān)鍵字記錄呢?憑什么把我從99分改為59分?對(duì)不對(duì)?我憑本事抄的答案你憑啥給我改成不及格?作為一個(gè)人,難道我連抄答案的自由都沒(méi)有了嗎?多么糟糕的組織會(huì)干出這樣無(wú)情的事?——摘自跨國(guó)出版物《星光日記》。 解決方案代碼如下▼
字典.Exists(關(guān)鍵字)
第4行代碼將數(shù)據(jù)源數(shù)據(jù)存入數(shù)組arr 第5至第7行代碼遍歷數(shù)組元素,將姓名作為key,特長(zhǎng)作為item,分別存入字典。 …… 4 丨 如何將數(shù)據(jù)從字典取出 Sub 讀取數(shù)據(jù)() Dim d As New Dictionary '聲明一個(gè)字典對(duì)象 d('看見(jiàn)星光') = 99 d('Excel星球') = 98 MsgBox d('Excel星球')End Sub
![]() 如上圖所示的數(shù)據(jù)為例,需要根據(jù)A:B列的數(shù)據(jù)源,查詢D列人名對(duì)應(yīng)的特長(zhǎng),這就是所謂的條件查詢了。 Sub 讀取數(shù)據(jù)2() Dim d As New Dictionary Dim arr, brr, i As Long arr = Range('a1').CurrentRegion '數(shù)據(jù)源 For i = 2 To UBound(arr) '遍歷數(shù)組,數(shù)據(jù)裝入字典 d(arr(i, 1)) = arr(i, 2) 'key是人名,item是特長(zhǎng) Next brr = Range('d1:e' & Cells(Rows.Count, 'd').End(xlUp).Row) '查詢區(qū)域 For i = 2 To UBound(brr) '遍歷查詢值 If d.Exists(brr(i, 1)) Then '如果字典存在查詢值 brr(i, 2) = d(brr(i, 1)) '獲取人名對(duì)應(yīng)的條目 Else brr(i, 2) = '查無(wú)此人' End If Next Range('d1:e' & Cells(Rows.Count, 'd').End(xlUp).Row) = brr Set d = NothingEnd Sub![]() 字典除了支持通過(guò)指定關(guān)鍵字查詢對(duì)應(yīng)Item外,也支持一次性將所有的關(guān)鍵字或條目轉(zhuǎn)換為一維數(shù)組,這需要用到字典的Keys和Items屬性。 獲取字典所有的Key,語(yǔ)法格式如下▼ 獲取字典所有的Item,語(yǔ)法格式如下▼ 字典.Items…… 我用麒麟雙臂給大家舉個(gè)典型的例子:數(shù)據(jù)去重。 如下圖所示的數(shù)據(jù)表,A列人名存在重復(fù)值,需要去重復(fù),獲取不重復(fù)的人員名單,結(jié)果如D列。
第4行至第9行代碼將數(shù)據(jù)源的姓名作為關(guān)鍵字存入字典。很多新手朋友可能困惑于下面這句代碼。 d(arr(i, 1)) = ''
需要重點(diǎn)說(shuō)一下第11行代碼,它的作用是將字典所有關(guān)鍵字存入當(dāng)前工作表的D列。語(yǔ)句如下。 Range('d1').Resize(d.Count, 1) = Application.Transpose(d.Keys)Range('d1').Resize(d.Count, 1),以d1單元格為起點(diǎn),向下擴(kuò)展指定行數(shù),作為存放字典所有關(guān)鍵字的單元格區(qū)域。 d.Keys是以一維數(shù)組的形式返回字典所有的關(guān)鍵字。在數(shù)組四篇里咱們講過(guò),一維數(shù)組就是一行數(shù)據(jù),需要通過(guò)Transpose函數(shù)進(jìn)行一次轉(zhuǎn)置才能轉(zhuǎn)換為二維,然后才能直接寫入垂直單元格區(qū)域。 綜上所述——這四個(gè)字老霸氣了,但更霸氣的是隨后的四個(gè)字——您就懂了! …… 擴(kuò)展一下,我再舉個(gè)與之相似的例子。 如下圖所示,需要在D:E列,獲取A:B列不重復(fù)的人名及其特長(zhǎng)數(shù)據(jù)。 ![]()
和上一段代碼相比,所不同的有兩句。 第11行代碼放置數(shù)據(jù)的列數(shù)由1列改為了2列。 Range('d1').Resize(d.Count, 2)代碼使用以下語(yǔ)句一次性獲取字典的Keys和Items,存入一個(gè)一維數(shù)組(在數(shù)組四篇里咱們講過(guò),Array函數(shù)結(jié)果為一維數(shù)組),最后再通過(guò)一次轉(zhuǎn)置將一維數(shù)組修改為2維,直接寫入指定單元格區(qū)域。 …… 5 丨 如何移除字典中的元素 如果移除指定關(guān)鍵字,可以使用方法Remove,語(yǔ)法格式如下▼ 字典.Remove 關(guān)鍵字示例代碼如下:
第3行和第4行代碼分別添加兩個(gè)關(guān)鍵字到字典中。此時(shí)字典有兩行數(shù)據(jù),如下圖所示▼ ![]() ![]() 除了根據(jù)指定關(guān)鍵字移除數(shù)據(jù)外,還可以使用方法RemoveAll將字典數(shù)據(jù)一次性清空。語(yǔ)法格式如下▼ 字典.RemoveAll
![]() 示例代碼如下▼
第6行代碼在實(shí)際處理每行數(shù)據(jù)之前,先清空字典中的所有元素。 第7至第11行代碼遍歷當(dāng)前行每列的元素,只將唯一值存入字典。 第12行代碼使用Join函數(shù),以逗號(hào)為分隔符,將當(dāng)前字典所有的關(guān)鍵字合并成為一個(gè)字符串,并存入結(jié)果數(shù)組。 arr(i, UBound(arr, 2)) = VBA.Join(d.Keys, ',')然后再遍歷處理下一行數(shù)據(jù)…… …… 6 丨 如何遍歷字典中的元素 有時(shí)候,為了篩選出符合條件的數(shù)據(jù),我們需要像遍歷數(shù)組一樣,遍歷字典中的每個(gè)元素。這通常需要先獲取字典的Keys集合,再遍歷每個(gè)Key去篩選字典中符合條件的數(shù)據(jù)。 什么意思呢?舉個(gè)例子還是我。 如下圖所示的數(shù)據(jù)表,需要篩選出人名重復(fù)出現(xiàn)次數(shù)大于2次的人員名單,以及相關(guān)出現(xiàn)次數(shù),結(jié)果參考C:D列。
代碼解析: 第4行代碼將數(shù)據(jù)源數(shù)據(jù)存入數(shù)組arr。 第5行至第7行代碼將數(shù)組arr中的人名存入字典,重點(diǎn)是下面這句代碼。 d(arr(i, 1)) = d(arr(i, 1)) + 1這句代碼類似于咱們?cè)谑裁词亲兞坷镏v過(guò)的計(jì)數(shù)器k=k+1。 作為賦值語(yǔ)句,它首先運(yùn)算的是等號(hào)右側(cè)的表達(dá)式:d(arr(i,1))+1。有趣的是,在代碼運(yùn)行這里的d(arr(i,1))的時(shí)候,我們還沒(méi)有將arr(i,1)的關(guān)鍵字存入字典,所以正常理解,這句代碼應(yīng)該返回程序錯(cuò)誤,但事實(shí)并沒(méi)有。字典(關(guān)鍵字)語(yǔ)句的運(yùn)算規(guī)則是,如果字典中存在指定關(guān)鍵字,則返回對(duì)應(yīng)的Item,否則會(huì)將該關(guān)鍵字存入字典,同時(shí)將其對(duì)應(yīng)的Item設(shè)置為Nothing。 等于▼ d(arr(i, 1)) = Nothing + 1等于▼ 也就是在字典中存入一個(gè)關(guān)鍵字arr(i,1),對(duì)應(yīng)的條目為1。 當(dāng)出現(xiàn)第2次出現(xiàn)同名的關(guān)鍵字時(shí)… d(arr(i, 1)) = d(arr(i, 1)) + 1等于▼ 等于▼ d(arr(i, 1)) = 2也就是在字典將關(guān)鍵字arr(i,1)對(duì)應(yīng)的條目更新為2. ……以此實(shí)現(xiàn)了相同值出現(xiàn)次數(shù)在字典中不斷累加的效果。 你品品,是不是這個(gè)道理?品不出來(lái)?沒(méi)事,不怕你墮落,我送你一瓶82年的雪碧,你慢慢品。 ![]() …… 第8行代碼返回字典中所有的Key,結(jié)果是一個(gè)下標(biāo)為0的一維數(shù)組,命名為aKey。 第9行代碼聲明一個(gè)結(jié)果數(shù)組,行數(shù)為字典的個(gè)數(shù),列數(shù)是2列,一列放人名,一列放次數(shù)。 第10行至第16行代碼采用索引的方式遍歷數(shù)組aKey,查看每一個(gè)Key在字典中的Item是否大于2次,如果大于2次則將Key和Item分別存入結(jié)果數(shù)組。相似的套路咱們?cè)跀?shù)組4篇的數(shù)組運(yùn)算里詳細(xì)講過(guò)了,這里就不再敲擊鍵盤,免得浪費(fèi)它所剩無(wú)幾的生命力。 …… 7 丨 如何釋放字典內(nèi)存 在章節(jié)什么是變量里咱們講過(guò)了,它的作用是釋放對(duì)象變量所占用的內(nèi)存,提高代碼運(yùn)行效率,雖然它未必是必須的,但聰明的您最好像優(yōu)秀的我一樣,養(yǎng)成使用它的好習(xí)慣。 有朋友可能會(huì)問(wèn),我用字典.RemoveAll語(yǔ)句清空字典,是不是可以代替Set 字典=Nothing?答案是否定的,你失戀了,傷感的把合租的房子清空,和你一怒之下把房子給燒沒(méi)了,是兩個(gè)概念好吧?前者房子雖然空了,但還在,還能住新歡和舊愛(ài),后者是連房子都沒(méi)了,再愛(ài)也都煙消云散了……。 8 丨 思考題和其它 …… 1丨前期綁定和后期綁定的不同 在本章第2節(jié)如何創(chuàng)建字典,咱們講過(guò),有些屬性前期綁定是支持的,但后期綁定并不能使用。這個(gè)有些屬性,其實(shí)指的就是Items和Keys。 在前期綁定的情況下,我們可以使用以下語(yǔ)句讀取Keys集合的指定索引元素。 讀取字典Keys的第2個(gè)元素,并賦值為變量strKey..▼ strKey = d.Keys(1)但后期綁定并不支持運(yùn)行該語(yǔ)句,必須先將Key或Item轉(zhuǎn)換為數(shù)組才能夠索引遍歷。 讀取字典Keys的第2個(gè)元素,并賦值為變量strKey..▼
…… 2丨如何讓字典不區(qū)分字母大小寫 一種是設(shè)置字典的CompareMode屬性為TextCompar,示例代碼如下▼ Sub 不區(qū)分字母大小寫() Dim d As New Dictionary d.CompareMode = TextCompare d('a') = 1 MsgBox d('A')End Sub一種是將所有的字母統(tǒng)一轉(zhuǎn)換為大寫(UCase)或小寫(LCase),這種方式明顯修改了原值,因此通常不建議使用。示例代碼如下▼
…… 3丨字典對(duì)數(shù)據(jù)類型的態(tài)度是嚴(yán)格的 字典對(duì)數(shù)據(jù)類型的態(tài)度是嚴(yán)格的——這句話是什么意思呢?我們知道Excel是一款對(duì)數(shù)據(jù)類型要求很寬松的軟件,數(shù)值可以分為文本型數(shù)值和純數(shù)值兩種,在VBA的邏輯判斷中,文本型數(shù)值和純數(shù)值是相等的,比如以下代碼返回True。 Sub t() MsgBox '1' = 1End Sub但在字典中,純數(shù)值和文本型數(shù)值并不相等。 舉個(gè)例子,示例代碼如下▼
第3行代碼將純數(shù)值1作為Key存入字典。 第4行代碼判斷文本型數(shù)值1在字典中是否存在,結(jié)果返回False。 知道這個(gè)知識(shí)點(diǎn)有什么用? 當(dāng)你需要處理的Key有數(shù)值類型時(shí),最好將源數(shù)據(jù)和查詢值都統(tǒng)一轉(zhuǎn)換為文本的形式,避免踩坑。如何統(tǒng)一轉(zhuǎn)換為文本的形式?可以通過(guò)聲明一個(gè)字符串類型的變量,強(qiáng)制進(jìn)行轉(zhuǎn)換。 示例代碼如下▼ Sub 數(shù)據(jù)類型2() Dim d As New Dictionary Dim strKey As String '定義一個(gè)字符串類型的變量 strKey = '1' d(strKey) = '愛(ài)就一個(gè)字' strKey = '2' d(strKey) = '我要說(shuō)兩次' strKey = 1 '強(qiáng)制轉(zhuǎn)換為字符串 MsgBox d.Exists(strKey) '結(jié)果返回TrueEnd Sub4丨字典常用方法和屬性的另外表達(dá)方式 如果你看的VBA代碼多了,可能會(huì)看到有人使用以下方式往字典中添加關(guān)鍵字和對(duì)應(yīng)條目: 其中夏天是Key,吃冰棍是Item。 但這種方式在常規(guī)VBA代碼中我并不推薦使用,原因很簡(jiǎn)單,它不夠靈活。如果字典中已存在相同關(guān)鍵字,該語(yǔ)句會(huì)返回錯(cuò)誤值,而且它并不支持更新相關(guān)條目。當(dāng)然,最重要的是,它打字太多了,你數(shù)數(shù),相比下句代碼它多打了幾個(gè)字?別不把手指當(dāng)親骨肉啊同志們吶!! d('夏天')='吃冰棍'與之相似的還有下面幾種語(yǔ)句,同樣因?yàn)椴粔蜢`活,打字偏多等不推薦使用。
5丨如何學(xué)習(xí)更多VBA編程知識(shí)和技巧? |
|
|
來(lái)自: 5jia5 > 《Excle & VBA》