Мне нужен этот метод сопоставления, чтобы пропускать пустые ячейки и не включать их в качестве сопоставленного значения - PullRequest
0 голосов
/ 26 мая 2020

Этот код работает почти идеально. Проблема в том, что он включает пустые ячейки в свои «сопоставленные» результаты. Что мне нужно изменить, чтобы этот код игнорировал пустые ячейки? Ниже я приведу пример того, что происходит.

enter image description here

Sub MarkMatches()
    Const TopLeftCell As String = "A2"      ' change to match where your data are

    Dim Rng As Range                        ' data range
    Dim FirstRow As Long, FirstClm As Long
    Dim Data As Variant                     ' original data (2-D)
    Dim Arr As Variant                      ' data rearranged (1-D)
    Dim Tmp As Variant                      ' working variable
    Dim R As Long, R1 As Long               ' row counters
    Dim C As Long                           ' column counter
    Dim Count() As String                   ' match counter

    With Range(TopLeftCell)
        FirstRow = .Row
        FirstClm = .Column
    End With
    C = Cells(FirstRow, Columns.Count).End(xlToLeft).Column
    Set Rng = Range(Cells(FirstRow, FirstClm), _
                    Cells(Rows.Count, FirstClm).End(xlUp).Offset(0, C - FirstClm))
    Data = Rng.Value

    ReDim Arr(1 To UBound(Data))
    For R = 1 To UBound(Data)
        ReDim Tmp(1 To UBound(Data, 2))
        For C = 1 To UBound(Data, 2)
            Tmp(C) = Data(R, C)
        Next C
        Arr(R) = Tmp
    Next R

    ReDim Count(1 To UBound(Arr))
    For R = 1 To UBound(Arr) - 1
        For R1 = R + 1 To UBound(Arr)
            Tmp = 0
            For C = 1 To UBound(Arr(R))
                If Not IsError(Application.Match(Arr(R)(C), Arr(R1), 0)) Then
                    Tmp = Tmp + 1
                End If
            Next C
            If Tmp > 0 Then                 ' change to suit
                Tmp = Format(Tmp, "(0)") & ", "
                Count(R) = Count(R) & CStr(R1 + FirstRow - 1) & Tmp
                Count(R1) = Count(R1) & CStr(R + FirstRow - 1) & Tmp
            End If
        Next R1
    Next R

    For R = 1 To UBound(Count)
        If Len(Count(R)) Then Count(R) = Left(Count(R), Len(Count(R)) - 2)
    Next R
    ' set the output column here (2 columns right of the last data column)
    '   to avoid including this column in the evaluation
    '   it must be blank before a re-run
    Set Rng = Rng.Resize(, 1).Offset(0, UBound(Data, 2) + 1)
    Rng.Value = Application.Transpose(Count)
End Sub

Спасибо @Variatus за код и помощь!

1 Ответ

0 голосов
/ 27 мая 2020

Я пытался работать с вашим исходным кодом, но, честно говоря, очень запутался. В моем примере ниже будут показаны некоторые практические приемы, которые могут помочь (а также те, кто может просмотреть ваш код позже, включая вас самих!). Итак, вот список комментариев:

  1. Всегда используйте Option Explicit. Возможно, в вашем коде это уже есть, но я перечисляю его здесь для полноты картины.
  2. Создайте имена переменных, которые описывают, какие данные в нем содержатся. Ваш код делает это немного, но некоторые имена переменных трудно вписать в поток logi c. Моя идея в программировании - это всегда пытаться писать самодокументированный код. Таким образом, почти всегда ясно, что код пытается выполнить sh. Затем я воспользуюсь комментарием для блоков кода, где он может быть менее ясным. (Не попадайтесь в ловушку, добавляя к именам переменных префикс «типа» или что-то в этом роде; в конечном итоге это того не стоит.)
  3. Четкое описание проблемы всегда помогает. Это верно не только для получения помощи по SO, но и для вас самих. Мой последний комментарий к вашему сообщению выше, спрашивающий об описании проблемы, действительно все упростил. Это включает в себя описание того, что вы хотите, чтобы ваш вывод отображал.

В соответствии с описанием проблемы вам необходимо идентифицировать каждый уникальный элемент и отслеживать, в какой строке вы найдете этот элемент, чтобы вы могли создать отчет позже . A Dictionary - идеальный инструмент для этого. Прочтите о том, как использовать Dictionary, но вы должны иметь возможность следить за тем, что делает этот блок кода здесь (даже без всех предыдущих объявлений):

    For Each cell In dataArea.Cells
        If Not IsEmpty(cell) Then
            If items.Exists(cell.Value) Then
                '--- add this row to the list
                rowList = items(cell.Value) & "," & cell.Row
                items(cell.Value) = rowList
            Else
                '--- first time adding this value
                items.Add cell.Value, cell.Row
            End If
        End If
    Next cell

Легко увидеть, как logi c этого кода следует за описанием проблемы. После этого нужно просто просмотреть каждую строку в области данных и проверить каждое значение в этой строке, чтобы увидеть, существуют ли дубликаты в любой другой строке. Полный пример решения приведен ниже, чтобы вы могли изучить его и приспособить к своей ситуации.

Option Explicit

Sub IdentifyMatches()
    Dim ws As Worksheet
    Set ws = Sheet1

    Dim dataArea As Range
    Set dataArea = ws.Range("A1:F6")

    Dim items As Dictionary
    Set items = New Dictionary

    '--- build the data set of all unique items, and make a note
    '    of which row the item appears.
    '      KEY   = cell value
    '      VALUE = CSV list of row numbers
    Dim rowList As String
    Dim cell As Range
    For Each cell In dataArea.Cells
        If Not IsEmpty(cell) Then
            If items.Exists(cell.Value) Then
                '--- add this row to the list
                rowList = items(cell.Value) & "," & cell.Row
                items(cell.Value) = rowList
            Else
                '--- first time adding this value
                items.Add cell.Value, cell.Row
            End If
        End If
    Next cell

    '--- now work through the data, row by row and make the report
    Dim report As String
    Dim duplicateCount As Variant
    ReDim duplicateCount(1 To dataArea.Rows.Count)

    Dim dataRow As Range
    For Each dataRow In dataArea.Rows
        Erase duplicateCount
        ReDim duplicateCount(1 To dataArea.Rows.Count)

        Dim rowNumber As Variant
        For Each cell In dataRow.Cells
            If items.Exists(cell.Value) Then
                rowList = items(cell.Value)
                Dim rowNumbers As Variant
                rowNumbers = Split(rowList, ",")
                For Each rowNumber In rowNumbers
                    If rowNumber <> cell.Row Then
                        duplicateCount(rowNumber) = duplicateCount(rowNumber) + 1
                    End If
                Next rowNumber
            End If
        Next cell

        report = vbNullString
        For rowNumber = 1 To UBound(duplicateCount)
            If duplicateCount(rowNumber) > 0 Then
                report = report & rowNumber & "(" & duplicateCount(rowNumber) & ")" & ", "
            End If
        Next rowNumber
        '--- display the report in the next column at the end of the data area
        If Len(report) > 0 Then
            report = Left$(report, Len(report) - 2) 'removes the trailing comma and space
            dataRow.Cells(1, dataRow.Columns.Count + 1).Value = report
        End If
    Next dataRow

End Sub
...