麻辣家族討論版版's Archiver

greetingsfromtw 發表於 2016-10-20 13:53

[字典物件]單一欄位的值與多個陣列比對後並分類至不同欄位

各位前輩好,
小弟之前有在網上找到一個程式碼,
參考網址如下:
http://club.excelhome.net/thread-868892-1-1.htmlz
其中的實例4,
可將一欄內的值使用InStr去比對多個陣列的值,
假設字串有符合,可分類至不同欄位.

現在遇到的第一個問題是,
該程式碼是將陣列的值寫在程式之中,類似這樣:
brr1 = Array("ASUS", "Sony")
但這在實作上是不太可能這樣去運用的,通常會想要將儲存格內的值寫入陣列中,
而非一個一個手動打在程式碼內.

可是,若想將儲存格的值存至陣列中,類似這樣:
brr1 = Range("j2:j" & Cells(Rows.Count, 10).End(xlUp).Row)
則程式會出現錯誤,小弟實在不知該如何解決.

第二個問題是,
若原始資料內的值完全沒有符合陣列值任一字串的話,也會出現錯誤.

附上小弟修改後的程式碼及附件如下:

[attach]25586[/attach][code]Sub test1()
'參考網址:http://club.excelhome.net/thread-868892-1-1.htmlz
'實例4


Dim arr, myD1, myD2, myD3
arr = Range("a2:a" & Cells(Rows.Count, 1).End(xlUp).Row)

'若使用以下方式將儲存格對應至陣列則會出問題
'brr1 = Range("j2:j" & Cells(Rows.Count, 10).End(xlUp).Row)
'brr2 = Range("k2:k" & Cells(Rows.Count, 11).End(xlUp).Row)
'brr3 = Range("L2:L" & Cells(Rows.Count, 12).End(xlUp).Row)


brr1 = Array("ASUS", "Sony")
brr2 = Array("Samsung")
brr3 = Array("長江", "小米")

Set myD1 = CreateObject("Scripting.Dictionary")
Set myD2 = CreateObject("Scripting.Dictionary")
Set myD3 = CreateObject("Scripting.Dictionary")
Set myD4 = CreateObject("Scripting.Dictionary")

For x = 1 To UBound(arr)

For i = 0 To UBound(brr1)
If InStr(arr(x, 1), brr1(i)) > 0 Then
myD1(arr(x, 1)) = ""
GoTo 100
End If
Next i

For j = 0 To UBound(brr2)
If InStr(arr(x, 1), brr2(j)) > 0 Then
myD2(arr(x, 1)) = ""
GoTo 100
End If
Next j


For k = 0 To UBound(brr3)
If InStr(arr(x, 1), brr3(k)) > 0 Then
myD3(arr(x, 1)) = ""
GoTo 100
End If
Next k

myD4(arr(x, 1)) = ""

100:
Next x


Range("b2").Resize(UBound(myD1.keys) + 1, 1) = Application.Transpose(myD1.keys)
Range("c2").Resize(UBound(myD2.keys) + 1, 1) = Application.Transpose(myD2.keys)
Range("d2").Resize(UBound(myD3.keys) + 1, 1) = Application.Transpose(myD3.keys)
Range("e2").Resize(UBound(myD4.keys) + 1, 1) = Application.Transpose(myD4.keys)


End Sub

[/code]若可以的話希望前輩能夠不吝指點迷津,十分感謝.

准提部林 發表於 2016-10-20 16:41

[i=s] 本帖最後由 准提部林 於 2016-10-20 16:43 編輯 [/i]

[code]Sub U_Test()
Dim xR As Range, xD, Arr, Brr, Mx&, N%, G(1 To 4), DK
Arr = Range([A2], Cells(Rows.Count, 1).End(xlUp))
Set xD = CreateObject("Scripting.Dictionary")
For Each xR In [J2:L40]
    If xR <> "" Then xD(UCase(xR)) = xR.Column - 9 '關鍵字依其欄位帶序號
Next
ReDim Brr(1 To UBound(Arr), 1 To 4)

For i = 1 To UBound(Arr)
    N = 4 '預設序號為4
    For Each DK In xD.keys
        If InStr(UCase(Arr(i, 1)), DK) Then N = xD(DK): Exit For '有符合,取出序號
    Next
    G(N) = G(N) + 1 '依序號不同, 各自累計欄位的筆數
    If G(N) > Mx Then Mx = G(N) '取得最大筆數
    Brr(G(N), N) = Arr(i, 1) '按序號及筆數填入資料到陣列
Next i
[B2].Resize(Mx, 4) = Brr
End Sub
[/code]

[b][color=blue]InStr 會區分英文大小寫, 必須使用 UCase 統一轉為大寫(LCase也可)[/color][/b]

greetingsfromtw 發表於 2016-10-20 16:50

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94904&ptid=18600]2#[/url] [i]准提部林[/i] [/b]

非常感謝淮提部林前輩提供解答,比原本的程式碼縮減很多,小弟嘆服.
目前還在努力鑽研字典物件中,
一定努力學習,
希望有朝一日能及得上前輩的萬分之一.

Hsieh 發表於 2016-10-20 23:38

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94905&ptid=18600]3#[/url] [i]greetingsfromtw[/i] [/b][code]Sub ex()
Set d = CreateObject("Scripting.Dictionary")
Set Rng = Range("J1").CurrentRegion.SpecialCells(xlCellTypeConstants) '比對陣列
For Each a In Range([A2], [A2].End(xlDown)) '原始資料迴圈
  For Each c In Rng
     If InStr(UCase(a), UCase(c)) > 0 Then
        d(c.Column) = "" '記住比對到陣列的欄位
     End If
  Next
  If d.Count > 0 Then '表示原始資料比對成功
     For Each ky In d.keys
       Cells(65536, ky - 8).End(xlUp).Offset(1, 0) = a
     Next
     d.RemoveAll '清空字典
     Else
     Cells(65536, "E").End(xlUp).Offset(1, 0) = a '比對不成功
  End If
Next
End Sub[/code]

GBKEE 發表於 2016-10-21 08:06

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94905&ptid=18600]3#[/url] [i]greetingsfromtw[/i] [/b]
不用字典物件也可以試試看[code]Option Explicit
Sub Ex()
    Dim Rng As Range, R As Range, E As Range, i(1 To 2) As Integer
    With Range("J1").CurrentRegion
        i(1) = .Columns.Count + 1
        Set Rng = .Rows("2:" & .Rows.Count).SpecialCells(xlCellTypeConstants)  '比對字串
    End With
    Range([A2], [A2].End(xlDown)).Offset(, 1).Resize(, i(1)) = ""
    For Each R In Range([A2], [A2].End(xlDown)) '原始資料迴圈
            i(2) = i(1)
            For Each E In Rng
                If InStr(UCase(R), UCase(E)) Then
                    i(2) = E.Column - Range("J1").Column + 1
                    Exit For
                End If
            Next
          With Cells(65536, 1 + i(2))
            If .EntireColumn.Find(R, Lookat:=xlPart) Is Nothing Then .End(xlUp).Offset(1) = R
            '資料不重複
          End With
    Next
End Sub
[/code]

greetingsfromtw 發表於 2016-10-21 10:44

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94915&ptid=18600]4#[/url] [i]Hsieh[/i] [/b]

非常感謝Hsieh前輩特地提供字典物件的另類解法,
也是將原程式碼縮減一大部份,令人佩服.
此論壇真是高手雲集,小弟一定努力學習.

starbox520 發表於 2016-10-21 10:52

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94915&ptid=18600]4#[/url] [i]Hsieh[/i] [/b]


    比對的話可以用在ListBox裡面嗎
    等於我反白裡面其中1項資訊
    讓他對應後去做篩選
    這是可行的嗎

greetingsfromtw 發表於 2016-10-21 11:00

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94919&ptid=18600]5#[/url] [i]GBKEE[/i] [/b]

非常感謝GBKEE前輩願意用其他方式來提供此問題的解法,
原始的程式碼較為冗長,
但三位前輩均輕描淡寫地用少量程式碼寫出漂亮解答,
此論壇實在是臥虎藏龍,有許多excel高手前輩俠隱於此,
而且還願意無私指點像新手,小弟銘感五內.

讚賞之言說得過多也擔心有浮誇之嫌,

小弟收入不是太多,所以可能沒辦法持續贊助高額費用,
但願意以"少額贊助"與"努力學習",
來分擔站長大人維護此論壇的設備費用與回應板上前輩們無私指點的恩德.
再次感謝.

准提部林 發表於 2016-10-21 15:49

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94924&ptid=18600]8#[/url] [i]greetingsfromtw[/i] [/b]


1.能贊助即可貴,這也幫助他人可以在此獲得知識
2.4樓超板的方法也許較符實情(以篩選而言),同一文字包含兩個關鍵字時,則兩次分別取出,
  可測試範例檔的兩個程式去比較
3.另提供一個方便針對選取單一關鍵字的篩選法

[attach]25594[/attach]
 
 

greetingsfromtw 發表於 2016-10-21 19:42

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94930&ptid=18600]9#[/url] [i]准提部林[/i] [/b]

非常謝謝淮提部林前輩的回應.

雖然本身貢獻的不是很多,就是希望能夠對論壇有些微幫助.
小弟有將三位前輩的方式都有稍微測試,說真的,雖然小弟逛過的相關論壇很少,
但前輩們的程式碼和我在其他地方看到的寫法可說是完全不同.相當驚人,值得研究.

因目前本身的VBA程度太差,所以現在可能是試著模仿前輩的寫法,
先去寫一些基本的功能這樣,雖然是笨方法,不過起步總是痛苦的.
只能說光用看的跟自己寫是完全兩回事阿....還得再努力.

另外也十分感謝前輩所提供的附檔,
滑鼠點兩下直接篩選的這個功能實在太猛了,前所未見,
小弟斗膽,不知是否方便麻煩前輩指點是怎麼做到的?
好像不是寫在巨集中,也沒有使用儲存格公式.

准提部林 發表於 2016-10-21 19:50

[i=s] 本帖最後由 准提部林 於 2016-10-21 19:53 編輯 [/i]

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94936&ptid=18600]10#[/url] [i]greetingsfromtw[/i] [/b]


那是工作表〔事件〕觸發程式,按Alt + F11,對工作表物件按兩下

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim xR As Range
With Target
  If Intersect([J2:L8], .Cells) Is Nothing Then Exit Sub
  If .Value = "" Then Exit Sub
  Cancel = True
  For Each xR In Range([A2], Cells(Rows.Count, 1).End(xlUp))
    If InStr(UCase(xR), UCase(.Value)) > 0 Then
     Cells(Rows.Count, "G").End(xlUp)(2) = xR
    End If
  Next
End With
End Sub

greetingsfromtw 發表於 2016-10-21 19:53

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94937&ptid=18600]11#[/url] [i]准提部林[/i] [/b]



了解,有看到了,是寫在工作表內,不好意思,小弟再研究一下.感謝前輩提醒.

Andy2483 發表於 2023-5-10 14:51

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94930&ptid=18600]9#[/url] [i]准提部林[/i] [/b]


    謝謝論壇,謝謝前輩
後學藉此帖學習前輩的方案1,方案學習心得註解如下,請前輩再指導

執行前:
[attach]36332[/attach]

執行結果:
[attach]36333[/attach]


Sub ex()
[B2:E200].ClearContents
[color=SeaGreen]'↑清除結果欄舊資料[/color]
Set d = CreateObject("Scripting.Dictionary")
[color=SeaGreen]'↑令d變數是字典[/color]
Set Rng = Range("J1").CurrentRegion.SpecialCells(xlCellTypeConstants)
[color=SeaGreen]'↑令Rng變數是 [J1]串並聯儲存格擴展範圍的非空白格  ('比對陣列)[/color]
For Each a In Range([A2], [A2].End(xlDown))
[color=SeaGreen]'↑設逐項迴圈!令a變數是A欄裡的儲存格 ('原始資料迴圈)[/color]
  For Each c In Rng
[color=SeaGreen]  '↑設逐項迴圈!令c變數是Rng變數裡的儲存格[/color]
     If InStr(UCase(a), UCase(c)) > 0 Then
[color=SeaGreen]     '↑如果a變數儲存格值轉換成英文大寫的新字串後,
     '裡面有包含 c變數儲存格值轉換成英文大寫的新字串[/color]
        d(c.Column) = ""
[color=SeaGreen]        '↑令以c變數欄位數當key,item是空字元,納入d字典裡
        '('記住比對到陣列的欄位)[/color]
     End If
  Next
  If d.Count > 0 Then
[color=SeaGreen]  '↑如果d字典key數量>0 ?('表示原始資料比對成功)[/color]
     For Each ky In d.keys
[color=SeaGreen]     '↑設逐項迴圈!令ky是d字典裡的一個key[/color]
       Cells(65536, ky - 8).End(xlUp).Offset(1, 0) = a
[color=SeaGreen]       '↑令結果欄的第一個空白格是 a變數(儲存格值)[/color]
     Next
     d.RemoveAll
[color=SeaGreen]     '↑清空d字典[/color]
     Else
     Cells(65536, "E").End(xlUp).Offset(1, 0) = a
[color=SeaGreen]     '↑比對不成功!就將a變數(儲存格值)放在E欄第一個空白格[/color]
  End If
Next
End Sub

Andy2483 發表於 2023-5-10 15:42

[b]回復 [url=http://forum.twbts.com/redirect.php?goto=findpost&pid=94930&ptid=18600]9#[/url] [i]准提部林[/i] [/b]


    謝謝前輩
後學藉此帖學習前輩方案2(不重複),方案學習心得註解如下,請前輩再指導

執行結果:
[attach]36334[/attach]


Sub U_Test()
Dim xR As Range, xD, Arr, Brr, Mx&, N%, G(1 To 4), DK
[color=SeaGreen]'↑宣告變數[/color]
[B2:E200].ClearContents
[color=SeaGreen]'↑清除結果欄舊資料[/color]
Arr = Range([A2], Cells(Rows.Count, 1).End(xlUp))
[color=SeaGreen]'↑令Arr變數是二維陣列,以A欄儲存格值(原始資料)帶入陣列裡[/color]
Set xD = CreateObject("Scripting.Dictionary")
[color=SeaGreen]'↑令xD變數是字典[/color]
For Each xR In [J2:L40]
[color=SeaGreen]'↑令設逐項迴圈!令xR是範圍儲存格裡的一格[/color]
    If xR <> "" Then xD(UCase(xR)) = xR.Column - 9
   [color=Green] '↑如果xR變數不是空的!就令其轉換為大寫英文當key,item是其欄數-9,
    '納入xD字典裡('關鍵字依其欄位帶序號)[/color]
Next
ReDim Brr(1 To UBound(Arr), 1 To 4)
[color=SeaGreen]'↑宣告Brr變數是二維空陣列,縱向範圍同Arr,橫向從1 到4[/color]
For i = 1 To UBound(Arr)
[color=SeaGreen]'↑設順迴圈[/color]
    N = 4
[color=SeaGreen]    '↑令N變數是 4 ('預設序號為4,是用來放不符合的資料)[/color]
    For Each DK In xD.keys
[color=SeaGreen]    '↑設逐項迴圈!令DK是xD字典裡的一個key[/color]
        If InStr(UCase(Arr(i, 1)), DK) Then N = xD(DK): Exit For
[color=SeaGreen]        '↑如果原始資料轉換英文大寫的新字串裡有包含DK變數!
        '就令N變數變更為 以DK變數查xD字典的item值('有符合,取出序號),
        '取出序號後就結束迴圈,代表不重複使用 原始資料[/color]
    Next
    G(N) = G(N) + 1
[color=SeaGreen]    '↑依序號不同, 各自在G這一維陣列裡 累計欄位的筆數[/color]
    If G(N) > Mx Then Mx = G(N)
[color=SeaGreen]    '↑取得最大筆數[/color]
    Brr(G(N), N) = Arr(i, 1)
[color=SeaGreen]    '↑按序號及筆數填入資料到陣列[/color]
Next i
[B2].Resize(Mx, 4) = Brr
[color=SeaGreen]'↑令從[B2]開始擴展有資料的列數4欄,以Brr陣列值帶入[/color]
End Sub

頁: [1]

麻辣家族討論版版為 麻辣學園 網站成員  由 昱得資訊工作室 © Since 1993 所提供