跳到主要內容

Excel VBA (四) : 自訂函數之將某欄資料複製到其他位置(自動排序並濾掉重複的資料)

我有一個需求是將某欄的資料複製到其他地方, 但是希望能夠是按照大小排列, 並且過濾掉重複的資料

我先做一個測試, 如果我在A1輸入自訂函數(MyCopy), 去改變B1儲存格的內容 :
Function MyCopy() As Variant
    Cells(1, 2).Value = "AA"
    MyCopy = "OK"
End Function

結果是 #VALUE!(失敗), 再將程式修改一下, 在A1中輸入 =MyCopy(B1), 把B1當參數傳進去會怎樣
Function MyCopy(myRange As Range) As Variant
    myRange.Value = "AA"
    MyCopy = "OK"
End Function

結果依然是 #VALUE!(失敗), 只好爬文看看別人的作法, 爬了很久直到這篇文章(How to Change Another Cell with a VBA Function), 作者的技巧是用 Evaluate 函數來叫另一個自訂函數來幫忙修改其他儲存格的資料, 看來自訂函數果真無法直接修改其他儲存格的資料了!(如果有其他作法也歡迎留言告知喔), 程式再改一下測測看
Function MyCopy() As Variant
    Evaluate "MyChange(B1,100)"
    MyCopy = "OK"
End Function

Function MyChange(myRange As Range, newData As Variant)
    myRange.Value = newData
End Function

我在A1儲存格中輸入 =MyCopy(), 結果對了!!(附上圖慶祝一下)

再來是排序問題, 網路上作法很多, 最後我挑了 System.Collections.SortedList 這個物件, 程式如下 :
Function MyCopy(copyFrom As Range, copyTo As Range) As Variant
    Dim mList As Object
    Dim i As Integer
    Dim data As Variant
    
    Set mList = CreateObject("System.Collections.SortedList")   '建立排序物件
    '開始排序並且濾掉重複資料
    For Each data In copyFrom
        If data.Value <> "" Then
            '非空白的才處理
            If Not mList.ContainsKey(data.Value) Then
                mList.Add data.Value, ""  '新增
            End If
        End If
    Next

    i = 1
    '開始寫到新的位置
    For Each data In copyTo
        If i <= mList.Count Then
            '改變其他儲存格的方法
            Evaluate "MyChange(" + data.Address + ",""" + mList.GetKey(i - 1) + """)"
        Else
            Exit For
        End If
        
        i = i + 1
    Next
    
    MyCopy = "OK"
End Function

'改變其他儲存格的自訂函數
Function MyChange(myRange As Range, newData As Variant)
    myRange.Value = newData
End Function


結果還不錯, 嘗試修改A1..B6中的資料也會即時改變, 可是發現這有一個問題, 就是當我把A5的AA文字刪除後, 會變成下面畫面 :

結果會出現兩個資料D, 原因是當不重複資料總數減少時, 程式寫完結果並不會繼續清除下面之前的舊資料!! 由此可知一個真正完整不被人罵的程式要處理很多問題, 一個簡單的程式會變成很巨大, 所以我來處理這個問題看看:
Function MyCopy(copyFrom As Range, copyTo As Range) As Variant
    Dim mList As Object
    Dim i As Integer
    Dim data As Variant
    
    Set mList = CreateObject("System.Collections.SortedList")   '建立排序物件
    '開始排序並且濾掉重複資料
    For Each data In copyFrom
        If data.Value <> "" Then
            '非空白的才處理
            If Not mList.ContainsKey(data.Value) Then
                mList.Add data.Value, ""  '新增
            End If
        End If
    Next

    i = 1
    '開始寫到新的位置
    For Each data In copyTo
        If i <= mList.Count Then
            '改變其他儲存格的方法
            Evaluate "MyChange(" + data.Address + ",""" + mList.GetKey(i - 1) + """)"
        Else
            If data.Value <> "" Then
                '非空白的才處理
                Evaluate "MyChange(" + data.Address + ","""")"
            Else
                Exit For
            End If
        End If
        
        i = i + 1
    Next
    
    MyCopy = "OK"
End Function

'改變其他儲存格的自訂函數
Function MyChange(myRange As Range, newData As Variant)
    myRange.Value = newData
End Function

雖然解決了這個問題, 但是感覺處理的不是很好, 如果有好的想法歡迎留言喔! ^ ^



測試時間: 2020年07月30日
測試環境: Windows 10 家用版, Excel 2019 家用版

留言

這個網誌中的熱門文章

Excel VBA (二) : 自訂函數之自動加總(續)

繼續上篇的 Excel VBA (一) : 自訂函數之自動加總 來修改程式 加總時如果要將非數字的全部濾掉, 我利用 TypeName 這個函數來完成, 再將Excel的C欄資料除了文字型別以外, 加上日期以及布林型別, 測試結果如下 Function 我的加總(加總區域 As Range) As Double Dim data As Variant 我的加總 = 0 For Each data In 加總區域 If TypeName(data.Value) = "Double" Then 我的加總 = 我的加總 + data End If Next End Function 如果我現在很懶, 懶得選擇加總區域, 讓它自動將它上方的資料自動加總起來, 例如在A10中輸入自訂函數, 則會自動將A1到A9的儲存格都加總起來, 我的懶人程式如下 : Function 我的加總2() As Double Dim 目前儲存格 As Range Dim 目前列 As Integer Dim i As Integer Set 目前儲存格 = Application.Caller 目前列 = 目前儲存格.Row 我的加總2 = 0 For i = 1 To 目前列 - 1 Set 目前儲存格 = 目前儲存格.Offset(-1, 0) If TypeName(目前儲存格.Value) = "Double" Then 我的加總2 = 我的加總2 + 目前儲存格 End If Next End Function Application.Caller : 會取得函數計算的儲存格, 它的行跟列可以透過Column跟Row來取得 ( 參考文章 ) 加總則是從同欄的第一列開始加, 一直加到輸入的前一列, 也就是 Application.Caller.Row - 1 offset(-1,0) : 則表示要位移的位置, -1 表示垂直往上一格, 0 表示水平維持不變 我在A10輸入自...

Excel C# (一) : 增益集 (Add-Ins) 之自訂函數

Excel的自訂函數除了之前介紹的VBA方式外, 還可以用 JavaScript, C# 等, 我就來試試看用C#來開發 我爬文找到了這篇 Excel-DNA 裡有詳細的教學, 不過我還是自己照做一遍看看是否成功 ! 第一步先開啟 Visual Studio 2019, 專案選擇 "類別庫(.NET Framework)" 過來將專案名稱輸入 MyExcelAddIns, 其他不變按下 "建立" 第二步接著在 "工具"->"NuGet 套件管理員"->"管理方案的 NuGet 套件", 將 Excel-DNA 套件加入至專案中 第三步將專案中的 Class1.cs 檔案刪除, 自己新增一個類別 "MyFunctions", 然後我複製 https://excel-dna.net 該網站上的範例, 貼進來方便測試 第四步我則是將方案組態由 "Debug" 改成 "Release"(如上圖的紅框), 再按 "建置" 下的 "重建方案" 產生 Excel XLL 檔案, 再去方案總管的 MyExcelAddIns 上按右鍵, 選擇 "在檔案總管中開啟資料夾" 會看到下面第二張圖 過來我的習慣是將這個 Release 目錄整個複製, 貼到桌面方便測試 ! 第五步則是開啟空白的Excel, 按 "程式開發人員" 下的 "Excel增益集", 點擊瀏覽後, 選擇桌面上我剛剛的 Release 資料夾, 如果Excel是32位元則選 MyExcelAddIns-AddIn.xll 檔案, 如果是64位元版本則選擇 MyExcelAddIns-AddIn64.xll 檔案, 接著在 Excel 任一儲存格輸入 =SayHello("World!"), 結果出現 Hello World!, 表示一切都正確, 接著我可以開始建立自己的函數了! 最後我個人認為自訂函數的VBA與C#兩種方式, 其優缺點如下:VBA的優點...

Asp.Net Core Debug Source Code

寫過Asp.Net Core Web MVC應該知道裡面要使用很多的Middleware,如果要知道裡面的運作方式,就要看Source Code,或者想知道自己寫的程式哪裡出問題,有時候就必須Debug進Source Code中看變數的變化 如何Debug Source Code我是參考 DEBUGGING ASP.NET CORE 2.0 SOURCE CODE 這篇文章來設定的,作法如下: 1.點選[工具]的[選項]  2.展開[偵錯]的[一般],並將[啟用Just My Code]的選取方塊 取消 ,以及[啟用來源連接支援]的選取方塊 點選起來   3.接著點選[偵錯]的[符號],並將[Microsoft伺服器]的選取方塊 點選起來  (點選之後會出現提示視窗,因為載入這些符號,會導致一些效能上的影響,尤其是首次載入,讓我以為Visual Studio當機了...) 這樣就可以Debug Source Code了,接著開始測試看看 : 1.先開啟一個新的專案(我選擇Asp.Net Core Web 應用程式) 2.因為只是測試所以都用預設的, 一直按確定就好, 除了Asp.Net Core 3.1改成5.0(操作方式都一樣沒差, 我只選5.0只是為了節省下載符號的時間以及空間而已,如果我選3.1它會下載這個版本的符號一次, 下次我選5.0它又會下載這個版本的符號一次) 3.專案自動建好後, 於視窗右邊的 HomeController.cs 按兩下, 視窗左邊就會看到檔案的內容, 接著找到 Index 的 rertrn View(), 在這列的前面點一下即可設定中斷點, 就像下圖的左邊紅色圈圈一樣(因為要顧到初學者所以才會說的這麼仔細) 4.設定好中斷點(紅色圈圈)之後, 過來就按下 [ IIS Express ] 的執行(上圖中中間上面的黃色圈起來的地方), 或者按下 F5, 就會開始執行程式 5.如果是第一次執行, 可能會出現一個確認視窗, 要你確認是否IIS Express的SSL憑證?按下是即可, 下面一個視窗也請按是 6.接著畫面會停止不動像當機一樣, 不是當機只是第一次抓符號要很久, 只要耐心等候直到 return View() 由紅底變成黃底就跑完了, 黃色底的列代表程式目前執行到這,...