使用 vba 从 3 列宽数据 table 中获取特定数据

Getting specific data from a 3 column wide data table using vba

我在 A、B 和 C 列中有一些数据。在 A 列中,我有标识号。对于彼此下方的某些行,这些可以相同,但并非总是如此(这意味着可以说标识号 1025 只能在 1 行或 10 行中。)每个标识号将在 B 列和 C 列中有 1 个或多个条目。在 B 列中,有一些 5 位数字。这些在每一行中可以相同,也可以不同。最后,在C栏,有一些短代码。

我想从中得到的是 B 列中的一些数字。我想检查 B 列中是否有任何数字,而 C 列中没有代码 'HL',然后将它们在 C 列中 'HL' 的第一个条目旁边的 D 列中。如果有多个这样的数字,我仍然想将它们检索到 D 列中,用逗号分隔。

一些例子:

A          B     C
1025001  11001   HL
1025001  11001   Sl
1025001  11002   ZF
1025001  11001   FG

在这种情况下,数字'11002'是从B列中检索的,因为它在C列中没有代码'HL',并且将第一个'HL'放入D列中的行中] 具有相同标识符的条目。

最终结果:

A          B     C      D
1025001  11001   HL   11002
1025001  11001   Sl
1025001  11002   ZF
1025001  11001   FG

另一个包含更多行的示例:

A          B     C
1025001  11001   HL
1025001  11001   Sl
1025001  11002   ZF
1025001  11001   FG
2659856  26532   TU
2659856  26856   HL
2659856  26856   TU
3598745  34589   HL
3598745  36598   HL
4896523  48596   NK
4896523  49563   HL
4896523  41236   NK
4896523  41659   HL

结果:

A          B     C      D
1025001  11001   HL   11002
1025001  11001   Sl
1025001  11002   ZF
1025001  11001   FG
2659856  26532   TU
2659856  26856   HL   26532
2659856  26856   TU
3598745  34589   HL
3598745  36598   HL
4896523  48596   NK
4896523  49563   HL   48596, 41236
4896523  41236   NK
4896523  41659   HL

对于第一个识别号码 1025001,11002 的结果放在 D 列中,因为它在 C 列中没有该识别号码的代码 'HL'。

对于第二个,2659856,数字26532放在第二行,因为那是第一行,代码为'HL'.

对于第三个 3598745,没有条目,因为所有行都有代码 'HL'。

对于第四个,4896523,第二行中的两个条目,因为这两个数字没有代码 'HL',并且因为第二行是带有 'HL' 的第一个条目。

我试过自己写一个 Sub,但老实说我什至不知道如何开始。我已经在 VBA 中编写了一些代码,但是我没有足够的经验。

一种方式:

Sub Tester()

    Dim vA, vB, vC, currA, rw As Range, dict As Object, rng As Range, r As Long, s, k
    Dim ws As Worksheet
    Set ws = ActiveSheet
    
    Set rng = ws.Range("A1").CurrentRegion   'input data
    Set rng = rng.Resize(rng.Rows.Count + 1) 'include one empty row below to 
                                             '  ensure the last Id is accounted for
    
    currA = Chr(0) 'or any unlikely value...
    r = 0          'the first "HL" row for a given Id  
    For Each rw In rng.Rows
        vA = rw.Cells(1).Value
        vB = rw.Cells(2).Value
        vC = rw.Cells(3).Value
        
        If vA <> currA Then 'Change in ColA - record any previous values
            If Not dict Is Nothing And r > 0 Then
                s = ""
                For Each k In dict.keys
                    'only ColB numbers which had no associated HL
                    If dict(k) Then s = s & IIf(s <> "", ", ", "") & k
                Next k
                ws.Cells(r, 4).Value = s
            End If
            
            currA = vA
            r = 0
            Set dict = CreateObject("scripting.dictionary")
        End If
        
        'process the current row
        If r = 0 And vC = "HL" Then r = rw.Row 'record first "HL" row number
        If Not dict.exists(vB) Then
            dict.Add vB, vC <> "HL" 'True/False
        Else
            ' "cancel" ColB number if it has any associated HL
            If dict(vB) = True Then dict(vB) = vC <> "HL"
        End If
        
    Next rw
End Sub

写入不匹配

Option Explicit

Sub writeNoMatch()
    
    ' Constants
    Const srcFirstCell As String = "A1"
    Const srcNumberOfColumns As Long = 3
    Const tgtFirstCell As String = "D1"
    Const Criteria As String = "HL"
    Const Delimiter As String = ", "
    
    Dim rng As Range
    ' Define Last Cell Range ('rng').
    Set rng = Cells(Rows.Count, Range(srcFirstCell).Column) _
                               .End(xlUp).Offset(, srcNumberOfColumns - 1)
    ' Define Data Range ('rng').
    Set rng = Range(srcFirstCell, rng)
        
    ' Define Data Array ('Data').
    Dim Data As Variant
    Data = rng.Value
    
    ' Write the unique values and their number of occurrences in first column
    ' of Data Array to the Data Dictionary ('dict').
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    Dim i As Long
    For i = 1 To UBound(Data, 1)
        dict(Data(i, 1)) = dict(Data(i, 1)) + 1
    Next
    
    ' Declare additional variables for the For Each Next loop.
    Dim Key As Variant
    Dim StartRow As Long
    Dim EndRow As Long
    Dim uniSize As Long
    Dim HL As Variant
    Dim hlPos As Variant
    Dim hlVal As Long
    Dim ResultString As String
    ' Define Result Array ('Result').
    Dim Result As Variant
    ReDim Result(1 To UBound(Data, 1), 1 To 1)
    
    For Each Key In dict.Keys
        ' Calculate Start Row ('StartRow').
        StartRow = EndRow + 1
        ' Define the number of occurrences ('uniSize') of the current value
        ' in first column of Data Array.
        uniSize = dict(Key)
        ' Resize HL Array ('HL') accordingly.
        ReDim HL(1 To uniSize)
        ' Write values from third column to HL Array.
        For i = 1 To uniSize
            HL(i) = Data(StartRow + i - 1, 3)
        Next i
        ' Calculate the current HL Position ('hlPos').
        hlPos = Application.Match(Criteria, HL, 0)
        If Not IsError(hlPos) Then
        ' hlPos found.
            ' Adjust current HL Position to fit position in Data Array.
            hlPos = StartRow + hlPos - 1
            ' Define current HL Value ('hlVal') from the second column
            ' of Data Array.
            hlVal = Data(hlPos, 2)
            ' Initialize Resulting String ('ResultString').
            ResultString = ""
            ' Calculate End Row ('EndRow').
            EndRow = StartRow + uniSize - 1
            ' Calculate Resulting String.
            For i = StartRow To EndRow
                ' Check if current row is not the HL row.
                If i <> hlPos Then
                    ' Check if current value in second column is different than
                    ' HL Value.
                    If Data(i, 2) <> hlVal Then
                        ' Check if current value in third column is different
                        ' than Criteria.
                        If Data(i, 3) <> Criteria Then
                            ' Write Result String.
                            ResultString = ResultString & Delimiter _
                                         & CStr(Data(i, 2))
                        End If
                    End If
                End If
            Next
            ' Write Resulting String to Result Array ('Result').
            If ResultString <> "" Then
                Result(hlPos, 1) = Right(ResultString, Len(ResultString) _
                                                         - Len(Delimiter))
            End If
        Else
        ' hlPos not found.
        End If
    Next Key
    
    ' Write values from Result Array to Target Column Range.
    Range(tgtFirstCell).Resize(UBound(Data)).Value = Result
    
    ' Inform user.
    MsgBox "No-match data transferred.", vbInformation, "Success"
    
End Sub

我将把这个解决方案混合在一起只是为了提供不同的视角。这段代码当然不是最有效的,但我想展示的是如何构建算法。基本上只是从你在脑海中如何做开始。你在问题的最初解释中写下规则,从那里开始构建。你知道你将不得不遍历每一行。接下来开始构建 If/Then 比较。您将 运行 陷入“初始值”问题,这意味着当没有可比较的内容时如何处理循环的第一个 运行。你想怎么处理?有时一个简单的“if first 运行through then”就是答案,有时——正如我在这里所做的——你可以简单地假装它是一个像其他任何循环一样的循环。也许当你循环浏览时,你可能会遇到其他没有什么可比较的实例。

一旦代码全部构建并运行(就像我在这里所做的那样),您就可以返回并进行优化。也许使用字典或数组更好。也许构建一个 class 是值得的。这一切都取决于。但是当你遇到这样的事情时,只要开始写下 pseudo-code 就可以模仿你在评估你写下的规则时所做的事情。

下面是适用于您的问题的代码。正如我所说,它不是最有效的,它只是我在做上面刚刚描述的事情时突然想到的。我希望这有助于演示一种生成算法的方法。

<code>
Option Explicit
Private Sub CommandButton1_Click()
Dim ws As Worksheet 'It's always wise to put your worksheet into a variable for reference. Much faster
Set ws = ActiveSheet 'Set it to the active sheet.

Dim ID As String '- To hold col A value
Dim IDCount As Integer '-To track how many of the same ID we have
Dim NewID As Boolean '-To track if we switch to a new ID
Dim Key() As String '-Array needed since we can have more than one HL Key per example 3
Dim KeyCount As Integer '-An index for the Key array
Dim Code As String '-To hold col C value
Dim Results As String '-To store the results for output when we finish the ID section.
Dim Match As Boolean '-To track key matches

Dim rng As Range '-This will be the entire range of the worksheet
'I'm hard setting it here for the example data for ease.
'You will want to code this to be more dynamic, of course
Set rng = ws.Range(ws.Cells(2, 1), ws.Cells(14, 4))

Dim Row As Integer, Col As Integer '-I always have Row & Col when working with worksheets.  Habit.
Dim i As Integer, j As Integer 'Index variables for looping and array reference

'Initialize veriables before the loop.
IDCount = 0 'Clearly we start our counts at zero
Results = "" 'And clearly we do not want anything in the results to start with.
For Row = 2 To 15
    If ws.Cells(Row, 1) <> ID Then NewID = True ''The very first row will always be a "new" id.
    If NewID Then
        'First output the results if any
        'Find the First HL coded Row
        For i = 0 To IDCount
            'Row is the current row, -IDCount will reference the first ID in the section.
            'This is why we track the IDCount.
            'Here we want to find the first instance of "HL" to put the results into.
            'All we are doing here is incrementing i until we find "HL"
            If ws.Cells(Row - IDCount + i, 3) = "HL" Then Exit For
        Next i
        'Row-IDCount+i will reference the first instance of "HL"
        If Results <> "" Then
            ws.Cells(Row - IDCount + i, 4) = Results 'Store the results here
            Results = ""
        End If
        'Since we are done with the IDCount from the previous section, clear it.
        IDCount = 0 'Setting to 1 because we are already on the first instance of the new ID
        NewID = False: KeyCount = 0
        ID = ws.Cells(Row, 1) 'Store the new ID value
        IDCount = IDCount + 1
        Code = ws.Cells(Row, 3) 'Store the code value
        ReDim Key(1) 'Initialize the array to have 1 element
        If Code = "HL" Then
            Key(KeyCount) = ws.Cells(Row, 2) 'Insert the new Key
        Else 'If Code <> "HL"
            If Results = "" Then
                Results = ws.Cells(Row, 2)
            Else ' Results <> ""
                Results = Results & ", " & ws.Cells(Row, 2)
            End If
        End If 'Code = "HL" or not
    Else 'If NOT NewID
        'Here we have data to compare.
        IDCount = IDCount + 1 'We have and additional row with the same ID
        If ws.Cells(Row, 3) = "HL" Then
            'Add a key to the array
            KeyCount = KeyCount + 1
            ReDim Preserve Key(KeyCount) 'Add an element to the array, keeping everything.
            Key(KeyCount) = ws.Cells(Row, 2)
        Else
            'Must loop through the section to check if non-"HL" cell matches any stored HL keys
            Match = False
            For j = 0 To KeyCount
                If Key(j) = ws.Cells(Row, 2) Then Match = True
            Next j
            If Match = False Then
                If Results = "" Then
                    Results = ws.Cells(Row, 2)
                Else
                    Results = Results & ", " & ws.Cells(Row, 2)
                End If 'Results = "" or not
            End If 'Match is true or false
        End If 'cell = "HL" or not
    End If 'NewID = true or false
Next Row

结束子'CommandButton1_Click