在具有多个条件的二维数组中搜索数值。

huangapple go评论96阅读模式
英文:

Search values in a two dimensional array with multiple criteria

问题

假设我有以下包含三列的表格。我想要在Column1是给定值的条件下,从Column3中搜索精确匹配或下一个前一个日期。

这可以通过XLOOKUP轻松完成。但是,我需要在VBA中完成,因为我将在用户窗体文本框中显示找到的日期。根据我迄今为止的搜索,Application.Worksheetfunction.Xlookup 对于多个条件中的 & 不起作用,因此解决此问题将涉及操纵数组。

我通过以下方式从该表格创建了一个变体:

Dim TBL As ListObject
Set TBL = Sheets("sheet1").ListObjects("Table1")
Dim DirArray As Variant
DirArray = TBL.DataBodyRange

有关如何使用数组获得近似匹配的任何建议?

英文:

Suppose I have the following table with three columns. I want to search for an exact match or next previous date from Column3, conditional to Column1 being a given value.

This can be easily done with XLOOKUP. However, I need to do so in VBA because I'll show the date found in a userform Textbox to the user. From what I have searched so far, Application.Worksheetfunction.Xlookup won't work with an & for multiple criteria, so the solution for this would involve manipulating arrays.

在具有多个条件的二维数组中搜索数值。

I created a variant from that table by writing:

Dim TBL As ListObject
Set TBL = Sheets("sheet1").ListObjects("Table1")
Dim DirArray As Variant
DirArray = TBL.DataBodyRange

Any advice on how to get that approximate match using arrays?

答案1

得分: 1

以下是翻译后的代码部分:

Option Explicit

Public Function findEntryByCol1andCol3(dataToSearch As Range, findCol1, findCol3) As Variant

    ' 用于存储最接近条件的行的变量
    Dim matchRow As Range
    Set matchRow = Nothing

    ' 用于正在检查的行的变量
    Dim checkRow As Range

    Dim ix As Long
    For ix = 1 To dataToSearch.Rows.Count
        ' 获取要检查的下一行
        Set checkRow = dataToSearch.Rows(ix)

        ' 此行的第一列是否与第一列的搜索条件匹配?
        If checkRow.Cells(1, 1).Value = findCol1 Then

            ' 现在检查行中的日期是否小于搜索日期
            If findCol3 >= checkRow.Cells(1, 3).Value Then

                ' 如果尚未找到匹配项,则使用此已检查的行作为第一个找到的匹配项
                If matchRow Is Nothing Then
                    Set matchRow = checkRow

                ' 如果已经有先前的匹配项,请检查新日期是否比先前找到的日期晚
                ElseIf matchRow.Cells(1, 3).Value < checkRow.Cells(1, 3).Value Then

                    Set matchRow = checkRow

                End If
            End If
        Else

        End If

    Next ix

    ' 现在返回搜索的结果
    If matchRow Is Nothing Then
        findEntryByCol1andCol3 = "未找到"
    Else
        findEntryByCol1andCol3 = matchRow.Cells(1, 3)
    End If

End Function

希望这对您有帮助!

英文:

There may well be a neater answer, but here is a simple brute-force function that just scans down every row in the given data looking for the closest match to the given criteria. The function returns the date of the closest match, but maybe it would be more useful to you if it returned, say, the row number of the row that is the closest match. Put this function in a new code module so that it can be called as a function from a cell, for example =findEntryByCol1andCol3(Table1,F1,F2)

Option Explicit

Public Function findEntryByCol1andCol3(dataToSearch As Range, findCol1, findCol3) As Variant

    &#39;// variable to hold the row with the closest match to criteria
    Dim matchRow As Range
    Set matchRow = Nothing
    
    &#39;// variable to hold the row being checked
    Dim checkRow As Range
    
    Dim ix As Long
    For ix = 1 To dataToSearch.Rows.Count
        &#39;// get the next row to be checked
        Set checkRow = dataToSearch.Rows(ix)
                
        &#39;// does column 1 in this row match the search criterion for column 1?
        If checkRow.Cells(1, 1).Value = findCol1 Then
            
            &#39;// now see if the date in the row is less than the search date
            If findCol3 &gt;= checkRow.Cells(1, 3).Value Then
                
                &#39;// If there has been no match then use this checked row as the first found match
                If matchRow Is Nothing Then
                    Set matchRow = checkRow
                    
                &#39;// If there has been a previous match check
                &#39;// if the new date is later that the previously found date
                ElseIf matchRow.Cells(1, 3).Value &lt; checkRow.Cells(1, 3).Value Then
                    
                    Set matchRow = checkRow
                    
                End If
            End If
        Else
        
        End If
        
    Next ix
    
    &#39;// Now return the result of the search
    If matchRow Is Nothing Then
        findEntryByCol1andCol3 = &quot;Not found&quot;
    Else
        findEntryByCol1andCol3 = matchRow.Cells(1, 3)
    End If
    
    
End Function

在具有多个条件的二维数组中搜索数值。

答案2

得分: 1

使用值数组将比每次引用单元格更快,尤其是如果您的表格要大得多。

您可以使用此函数 - 如果未找到有效日期,则将返回0。

由于我正在使用 sortBy,因此需要 Excel 365 才能使用此功能。

通过使用 SortBy,在找到匹配的日期后,可以安全地退出 for 循环。

Public Function nearestDate(lo As ListObject, valueColumn1 As String, valueColumn3 As Date) As Date

Dim arrValues As Variant
arrValues = Application.WorksheetFunction.SortBy(lo.DataBodyRange, lo.ListColumns(1).DataBodyRange, 1, lo.ListColumns(3).DataBodyRange, 1)

Dim i As Long
For i = 1 To UBound(arrValues, 1)
    If arrValues(i, 1) = valueColumn1 Then
        If arrValues(i, 3) = valueColumn3 Then
            '找到了我们要找的日期
            nearestDate = arrValues(i, 3)
        ElseIf arrValues(i, 3) < valueColumn3 Then
            '我们必须检查下一行 - 如果有的话
            If i < UBound(arrValues, 1) Then
                If arrValues(i + 1, 1) = valueColumn1 And arrValues(i + 1, 3) > valueColumn3 Then
                    '相同的列1,但列3大于valueColumn3
                    nearestDate = arrValues(i, 3)
                ElseIf arrValues(i + 1, 1) <> valueColumn1 Then
                    '新的列1值 --> 因此我们采用当前日期
                    nearestDate = arrValues(i, 3)
                End If
            Else
                '最后一个值 --> ok
                nearestDate = arrValues(i, 3)
            End If
        End If
    End If
    
    If nearestDate > 0 Then Exit For
Next

End Function

您可以像这样调用此函数:

Public Sub test()
Dim ws As Worksheet: Set ws = Thisworkbook.Worksheets("sheet1")

Dim lo As ListObject: Set lo = ws.ListObjects("Table1")
Dim valueColumn1 As String: valueColumn1 = ws.Range("F1")
Dim valueColumn3 As Date: valueColumn3 = ws.Range("F2")

Debug.Print nearestDate(lo, valueColumn1, valueColumn3)

End Sub
英文:

Using an array of values will be faster than referencing a cell for each check - esp. if your table is much larger.

You can use this function - it will return 0 in case no valid date is found.

As I am using sortBy you will need Excel 365 for this to work.

By using SortBy it is safe to exit the for-loop in case we have found a matching date.

Public Function nearestDate(lo As ListObject, valueColumn1 As String, valueColumn3 As Date) As Date

Dim arrValues As Variant
arrValues = Application.WorksheetFunction.SortBy(lo.DataBodyRange, lo.ListColumns(1).DataBodyRange, 1, lo.ListColumns(3).DataBodyRange, 1)

Dim i As Long
For i = 1 To UBound(arrValues, 1)
    If arrValues(i, 1) = valueColumn1 Then
        If arrValues(i, 3) = valueColumn3 Then
            &#39;we found what we are looking for
            nearestDate = arrValues(i, 3)
        ElseIf arrValues(i, 3) &lt; valueColumn3 Then
            &#39;we have to check next row - if there is one
            If i &lt; UBound(arrValues, 1) Then
                If arrValues(i + 1, 1) = valueColumn1 And arrValues(i + 1, 3) &gt; valueColumn3 Then
                    &#39;same column1 but column3 greater than valueColumn3
                    nearestDate = arrValues(i, 3)
                ElseIf arrValues(i + 1, 1) &lt;&gt; valueColumn1 Then
                    &#39;new column1 value --&gt; therefore we take current date
                    nearestDate = arrValues(i, 3)
                End If
            Else
                &#39;last value --&gt; ok
                nearestDate = arrValues(i, 3)
            End If
        End If
    End If
    
    If nearestDate &gt; 0 Then Exit For
Next

End Function

You can call this function like this:

Public Sub test()
Dim ws As Worksheet: Set ws = Thisworkbook.Worksheets(&quot;sheet1&quot;)

Dim lo As ListObject: Set lo = ws.ListObjects(&quot;Table1&quot;)
Dim valueColumn1 As String: valueColumn1 = ws.Range(&quot;F1&quot;)
Dim valueColumn3 As Date: valueColumn3 = ws.Range(&quot;F2&quot;)

Debug.Print nearestDate(lo, valueColumn1, valueColumn3)

End Sub


</details>



huangapple
  • 本文由 发表于 2023年1月8日 22:25:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75048518.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定