Найти столбец на основе заголовка столбца - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть этот код, и он работает только в том случае, если искомый заголовок находится в столбце B или «выше».
Допустим, у меня есть эта таблица, и мне нужно найти, какие столбцы «Имя» и «оценка»находится в.

Name    score
John       1
Joe        5

Если «Имя» в B1 и «Оценка» в C1, будет работать следующий код:

NameColumn = Split(Cells(1, Cells(1, 1).EntireRow.Find(What:="Name", LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByColumns, searchdirection:=xlNext, MatchCase:=True).Column).Address(True, False), "$")(0)
ScoreColumn = Split(Cells(1, Cells(1, 1).EntireRow.Find(What:="score", LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByColumns, searchdirection:=xlNext, MatchCase:=True).Column).Address(True, False), "$")(0)
                                                                ^^ <- search value

Приведенный выше код будет в случае возврата

NameColumn = "B"
ScoreColumn = "C"

Но предположим, что столбцы - это A & B, тогда он не найдет «Имя», потому что начнет поиск по ячейке 1,1 (A1), где находится заголовок.

ЧтоМогу ли я изменить это, чтобы сделать эту работу, или какие есть альтернативы возвращению «A» и «B» в приведенном выше примере?

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Вычисление буквы столбца заголовка

Вам потребуется добавить аргумент After в метод Find, указывающий на последнюю ячейку .Cells(.Cells.Count), чтобы начать поиск с первой ячейки .Cells(1).Но, как отметили chris neilsen в комментариях, это не причина, по которой ваш код потерпит неудачу, потому что он найдет Name в конце поиска.
Поскольку вы не определили, что«не работает» на самом деле означает, и очень маловероятно, что вы ошиблись Name в A1, я бы предположил, что NameColumn возвращает нежелательный результат (<>A), означающий, что вы использовали Name где-то ещев первой строке и вам действительно нужно начать поиск с первой ячейки .Cells(1).

Короткая версия

Sub FindAfterShort()

    Dim NameColumn As String   ' Name Column Letter
    Dim ScoreColumn As String  ' Score Column Letter

    With Rows(1)
        NameColumn = Split(.Find("Name", .Cells(.Cells.Count), xlValues, _
                xlWhole).Address, "$")(1)
        ScoreColumn = Split(.Find("Score", .Cells(.Cells.Count), xlValues, _
                xlWhole).Address, "$")(1)
    End With

    Debug.Print "Column Letters '" & NameColumn & "' and '" & ScoreColumn & "'."

End Sub

Предпочтительная версия

Sub FindAfterPref()

    Const cSheet As String = "Sheet1"   ' Worksheet Name

    Dim strName As String   ' Name Column Letter
    Dim strScore As String  ' Score Column Letter

    With ThisWorkbook.Worksheets(cSheet).Rows(1)
        strName = Split(.Find("Name", .Cells(.Cells.Count), xlValues, _
                xlWhole).Address, "$")(1)
        strScore = Split(.Find("Score", .Cells(.Cells.Count), xlValues, _
                xlWhole).Address, "$")(1)
    End With

    Debug.Print "Column Letters '" & strName & "' and '" & strScore & "'."

End Sub

A SmallЭтюд

Sub FindAfter()

    Const cSheet As String = "Sheet1"   ' Worksheet Name
    Const cFR As Long = 2               ' First Row
    ' The column where the Last Row Number will be calculated.
    Const cLRColumn As Variant = "A"    ' Last-Row Column Letter/Number

    Dim rngName As Range    ' Name Column Range, Name Range
    Dim rngScore As Range   ' Score Column Range, Score Range
    Dim lngName As Long     ' Name Column Number
    Dim lngScore As Long    ' Score Column Number
    Dim strName As String   ' Name Column Letter
    Dim strScore As String  ' Score Column Letter
    Dim lngLR As Long       ' Last Row Number (Calculated in Last-Row Column)

    With ThisWorkbook.Worksheets(cSheet).Rows(1)

        ' Find Method Arguments
        '   2. After: If you want to start the search from the first cell, you
        '             have to set the After parameter to the last cell. If you
        '             have the matching data in the first cell and you set the
        '             parameter to the first cell (default), it will still be
        '             found, but a little later (not mili, but micro seconds
        '             later) so it could be omitted.
        '   5. SearchOrder: Whenever a range is a one-row or a one-column range,
        '                   this argument can be omitted. Since you're searching
        '                   in a one-row range, "xlByRows" would have been the
        '                   more 'correct' way in this case.
        '   6. SearchDirection: This argument's parameter is by default "xlNext"
        '                       and can therefore be omitted
        '   7. MatchCase: This argument's parameter is by default "False". Since
        '                 I don't see the reason why you would have headers with
        '                 the same name, especially the ones you don't need
        '                 before the ones you need, it is omitted. If you really
        '                 need it, use "... xlWhole, , , True".
        Set rngName = .Find("Name", .Cells(.Cells.Count), xlValues, xlWhole)
        Set rngScore = .Find("Score", .Cells(.Cells.Count), xlValues, xlWhole)

        ' Address Arguments
        ' If the Address arguments are omitted, Range.Address returns the
        ' address as an absolute reference e.g. $A$1. When you split
        ' $A$1 you will get the following
        ' INDEX STRING
        '   0           - Empty string ("").
        '   1      A    - Use this i.e. index 1 for the split array index.
        '   2      1
        If Not rngName Is Nothing Then ' When "Name" was found.
            ' Calculate Name Column Number.
            lngName = rngName.Column
            ' Calculate Name Column Letter.
            strName = Split(rngName.Address, "$")(1)
        End If
        If Not rngScore Is Nothing Then ' When "Score" was found.
            ' Calculate Score Column Number.
            lngScore = rngScore.Column
            ' Calculate Score Column Letter.
            strScore = Split(rngScore.Address, "$")(1)
        End If

        Debug.Print "Column Numbers '" & lngName & "' and '" & lngScore & "'."
        Debug.Print "Column Letters '" & strName & "' and '" & strScore & "'."
        Debug.Print "Name Column Header Address '" & rngName.Address & "'."
        Debug.Print "Score Column Header Address '" & rngScore.Address & "'."

        With .Parent ' instead of "ThisWorkbook.Worksheets(cSheet)".

            '*******************************************************************
            ' This should demonstrate a case where you don't need the column
            ' letter (mostly you don't). You should use ".Cells", ".Range" is
            ' not an option.
            '*******************************************************************
            ' Column Number (lngName)
            ' Last Row Number calculated using Cells and lngName.
            If lngName <> 0 Then
                ' Calculate last row in Name Column.
                lngLR = .Cells(.Rows.Count, lngName).End(xlUp).Row
                ' Create a reference to the range from First Row to Last Row in
                ' Name Column.
                Set rngName = .Range(.Cells(cFR, lngName), _
                        .Cells(lngLR, lngName))
            End If

            '*******************************************************************
            ' This is the same as the previous and should demonstrate that
            ' when you already know the column letter, you have two choices:
            ' you can use ".Cells" or ".Range".
            '*******************************************************************
            ' Column Letter (strName)
            ' Last Row Number calculated using Cells and strName.
            If strName <> "" Then
                ' Calculate last row in Name Column.
                lngLR = .Cells(.Rows.Count, strName).End(xlUp).Row
                ' Create a reference to the range First Row to Last Row in
                ' Name Column.
                Set rngName = .Range(.Cells(cFR, strName), _
                        .Cells(lngLR, strName))
            End If
            ' Last Row Number calculated using Range and strName.
            If strName <> "" Then
                ' Calculate last row in Name Column.
                lngLR = .Range(strName & .Rows.Count).End(xlUp).Row
                ' Create a reference to the range from First Row to Last Row in
                ' Name Column.
                Set rngName = .Range(strName & cFR & ":" & strName & lngLR)
            End If
            '*******************************************************************

            ' Since the column letter is more user-friendly, the only use
            ' I can imagine where you might need it, is to inform the user e.g.
            MsgBox "Column '" & strName & "' contains the names and column '" _
                    & strScore & "' contains the scores.", vbInformation, _
                    "User Information"

        End With

        Debug.Print "Last (used) Row in Name Column '" & lngLR & "'."
        Debug.Print "Name Range Address '" & rngName.Address & "'."
        Debug.Print "Column '" & strName & "' contains the Names and column'" _
                & strScore & "' contains the scores."

    End With

End Sub
0 голосов
/ 12 февраля 2019

Вот быстрая функция UDF, которую я использовал в прошлом.

Возможно, это не лучший способ сделать это, но я использовал ее много лет.

Function ColumnHeaderLocation(entry As String, Optional ColumnNumber As Boolean)
Dim x, y As Long

y = ActiveSheet.Columns.Count
x = 1

Do Until x > y
    If ActiveSheet.Cells(1, x).Value = entry Then
        ColumnHeaderLocation = Split(ActiveSheet.Cells(1, x).Address(True, False), "$")(0)
        If ColumnNumber = True Then ColumnHeaderLocation = x
        Exit Function
    Else
        x = x + 1
    End If
Loop

End Function

Просто используйте имя заголовка столбца (см. Пример) ...

NameColumn = ColumnHeaderLocation("Name") ' returns Column Location as a letter

Или

NameColumn = ColumnHeaderLocation("Name", True) ' Returns column Location as a Number

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...