excel - 索引匹配其他表中的数字,如果找不到,则匹配前 3 位数字
问题描述
我有 3 个存储数据的表。我有第 4 个结果表,我根据我使用的 ref-ID 拉出至少 3 个结果表。请看下图:
第四张表图片:
这是我在 elec-rate 列上使用的代码 -
=MIN(IFERROR(INDEX($E$2:$G$50,MATCH($A3,$E$2:$E$50,0),3),100),IFERROR(INDEX($I$2:$K$50,MATCH($A3,$I$2:$I$50,0),3),100),IFERROR(INDEX($M$2:$O$50,MATCH($A3,$M$2:$M$50,0),3),100))
我的问题是 ref-ID 2990011/2990012,因为它返回 100 这意味着 ref-id 不存在于其他表中,因此它应该采用 refid 的前 3 位数字并与其他表的 3 位数字完全匹配并返回最小值价值。我怎么能做到这一点?任何指导表示赞赏。
解决方案
好的,这里是对UDF的介绍。如果该功能不能完全满足您的要求,请多多包涵。事实上,我不介意,因为目标不是提供解决方案,而是提供介绍。首先,这是一个代码转储。将其全部安装在工作簿的标准代码模块中。那是一个以前不存在的模块。您需要创建它,它的名称将是Module1
. 你可以重命名它。
代码仅凭其存在将无济于事。以 XLSM 格式保存您的工作簿。
Option Explicit
Private Sub TestUdf()
' 020
Dim Criteria As Range
Dim FirstTblID As Integer
Dim NumTables As Integer
Set Criteria = Range("A20")
FirstTblID = 1
NumTables = 3
Debug.Print Lowest(Criteria, FirstTblID, NumTables)
End Sub
Function Lowest(Criteria As Range, _
FirstTblID As Integer, _
NumTables As Integer) As Double
' 020
Dim Fun As Double ' function return value
Dim Crit As String ' value of the received range "Criteria"
Dim SearchRng As Range ' range to search in
Dim Fnd As Range ' Cell where match was found
Dim LookUp As Double ' individual lookup value
Dim i As Integer ' loop counter
Crit = Criteria.Value
For i = FirstTblID To (FirstTblID + NumTables - 1)
Set SearchRng = ActiveSheet.ListObjects("Table" & i).DataBodyRange.Columns(1)
Set Fnd = SearchRng.Find(What:=Crit, _
After:=SearchRng.Cells(SearchRng.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Fnd Is Nothing Then
' if the Find wasn't successful
LookUp = FindAverage(Crit, SearchRng)
Else
LookUp = Fnd.Offset(0, 2).Value
End If
If Fun = 0 Or LookUp < Fun Then Fun = LookUp
Next i
Lowest = Fun
End Function
Private Function FindAverage(ByVal Criteria As Variant, _
Rng As Range) As Double
' 020
Dim Fun As Double ' function return value
Dim Arr As Variant ' table data range
Dim Total As Double, n As Integer ' calculate average
Dim R As Long ' row counter
' truncate Criteria to the first 3 charcte4rs
Criteria = Left(Criteria, 3)
' expand the range to include the 3rd column
Arr = Rng.Resize(Rng.Rows.Count, 3).Value
' loop through all the cell values
For R = 1 To UBound(Arr)
' compare the first 3 characters case insensive
If InStr(1, Arr(R, 1), Criteria, vbTextCompare) = 1 Then
' skip if there is no value in the 3rd column
If Val(Arr(R, 3)) Then
Total = Total + Arr(R, 3)
n = n + 1
End If
End If
Next R
' return zero if no qualifying values were found
If n Then FindAverage = Total / n
End Function
也许您需要了解的第一件事是代码没有指定要查看的工作表。那是因为它是作为 UDF 设计的。它期望从调用它的工作表中获取指令。但是上面的第一个过程,被调用的过程不会TestUdf
从工作表中调用。您可以将其用于测试 - 经过一些调整。
该行Debug.Print Lowest(Criteria, FirstTblID, NumTables)
会将类似于您当前公式的结果的内容打印到即时窗口。这是 VB 编辑器屏幕底部的窗格。此函数调用有 3 个参数,您可以在过程中设置Criteria
,FirstTblID
和NumTables
。第一个是包含查找值的单元格,例如 2990001。另外两个用于显示灵活性。
我将您的 3 个表命名为Table1
,Table2
和Table3
。因此,第一个表是 #1,表的总数是 3。想法是您可以在表 14 到 18 上使用相同的函数。在这种情况下,FirstTblID
将是 14 和NumTables
5。该函数将返回最低的5个值。
在 VBA 中,所有这些都很好地展开并使用命名变量进行解释以提供指导和注释以提供帮助。当您从工作表中调用该函数时,没有多余的装饰。在这种情况下, Cell(A20) 位于第 4 个表格的第一列。该函数将进入第三列。从那里可以像任何其他 Excel 函数一样上下复制它。
[C20] = Lowest(A20, 1, 3)
现在,该函数返回从 3 个表中获取的 3 个值中的最小值(请记住重命名它们!)但是这里出现了您现在要询问的修正。这只是现有程序流程的一个小改动。这里是。
If Fnd Is Nothing Then
' if the Find wasn't successful
LookUp = FindAverage(Crit, SearchRng)
Else
LookUp = Fnd.Offset(0, 2).Value
End If
If Fun = 0 Or LookUp < Fun Then Fun = LookUp
在这里,您可以看到 VBA 通过合理的努力启用了一种与单一工作表函数支持完全不同的方法。上面的代码分别决定了三个值中的每一个。如果无法找到其中一个,则仅将一个值替换为基于前 3 位的相似值的平均值。我知道这不是你要的,但我想你可能不敢问。在 VBA 中,这很容易,当然,创建您确实要求的结果只是一个小改动。
在我的代码中,另一种计算方式包含在名为FindAverage
. 原始函数仅在需要时调用它,但出于维护目的,它是一个单独的函数,您可以将其提供给其他函数。事实上,您也可以从工作表中调用此函数。该调用如下所示,但您需要Private
从其声明行中删除 ,以便可以从工作表中看到它。
=FindAverage(A20,Table1[Ref-ID])
根据您的建议,这里是按名称命名表的代码。请注意,函数的名称(此处Lowest2
为 )在最后一行代码中重复出现。
Function Lowest2(Criteria As Range, _
FirstTblID As Integer, _
NumTables As Integer) As Double
' 020 (with named tables)
Dim Fun As Double ' function return value
Dim Tbls() As String ' list of tables
Dim Crit As String ' value of the received range "Criteria"
Dim SearchRng As Range ' range to search in
Dim Fnd As Range ' Cell where match was found
Dim LookUp As Double ' individual lookup value
Dim i As Integer ' loop counter
Tbls = Split("Table1,Table2,Table3,Table4,Table5", ",")
Crit = Criteria.Value
For i = FirstTblID To (FirstTblID + NumTables - 1)
Set SearchRng = ActiveSheet.ListObjects(Trim(Tbls(i - 1))).DataBodyRange.Columns(1)
Set Fnd = SearchRng.Find(What:=Crit, _
After:=SearchRng.Cells(SearchRng.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Fnd Is Nothing Then
' if the Find wasn't successful
LookUp = FindAverage(Crit, SearchRng)
Else
LookUp = Fnd.Offset(0, 2).Value
End If
If Fun = 0 Or LookUp < Fun Then Fun = LookUp
Next i
Lowest2 = Fun
End Function
我保持函数调用与原始函数调用相同,但添加了一行代码,您可以在其中指定表名。
Tbls = Split("Table1,Table2,Table3,Table4,Table5", ",")
无论您的表名称是什么,您都可以在此处列出它们,并以逗号分隔。您可以指定Split("Mary,Tom,Mary S,Thomas,Bangkok", ",")
.
现在,函数调用Lowest2(A20,1,3)
指定此列表中的第一个表为1
,表的数量为3
。这意味着列表中的 Table4 和 Table4 都未使用。但您也可以调用Lowest2(A20,1,5)
或实际上Lowest2(A20,1,1)
从一个表中获取单个值。您可以调用Lowest2(A20,3,3)
从表 3 到 5 中获取值。您无法从表 1、3 和 5 中获取值,但这也只是另一个小改动。
推荐阅读
- java - 数组列表
vs String[],如何以及何时使用? - python - 除了设置断点之外,还有什么方法(通过设置或扩展)来查看和使用 VSCODE 中的变量。喜欢蜘蛛?
- javascript - 为什么我不能在 JavaScript 中交换 2 个变量?
- javascript - 在 React 应用程序中格式化 PostgreSQL create_timestamp
- sqlite - 离子应用程序如何存储键和值
- python - 熊猫为字典值中列表的每个元素创建一列?
- java - Junit 使用扫描仪输入测试方法
- list - 从 Map 值生成的 Groovy groupby 列表
- python - Why is `to_numpy()` recognized as an attribute rather than a method of dataframe?
- machine-learning - 时间序列中的分类模式