提问人:Stuart_P 提问时间:11/15/2023 最后编辑:braXStuart_P 更新时间:11/15/2023 访问量:82
直到在 for 循环中向后退步?
Do until inside a for loop stepping backwards?
问:
我正在尝试为电子表格编写一些代码,以计算物料清单 (BoM) 中的项目是否是购买的装配的子项。我会尝试更详细地解释。
基本上我的电子表格有很多列,但只需要 2 列就可以计算。为简单起见,我只说这些是 A 列和 B 列,VBA 的输出放在 C 列中。
A 列包含 BoM 级别 - 这始终从级别 0 开始,此级别的子级是级别 1,级别 1 的子级是级别 2,依此类推。目前,在我创建的所有 BoM 中,10 级是“最深”的,但并非不可能增加。
B 列包含程序集状态 - 我对此列唯一感兴趣的值是“P”表示已购买。
由于我的电子表格中的各种标题,数据从第 12 行开始,由于这是顶级,我始终可以在 C 列中将其标记为“TL”。
现在我正在挣扎。从 13 行开始,每行(这很容易扩展到 100,000 行甚至更多!我需要查看 A 列(BoM 级别),然后向上查找下面的级别,直到找到已购买的级别(B 列中的“P”)或达到 BoM 级别 0。
希望这张表会有所帮助
A列 | B列 | c列 |
---|---|---|
0 | 免疫垫 | TL系列 |
1 | 免疫垫 | 假 |
2 | 假 | |
2 | 假 | |
1 | P | 假 |
2 | P | 真 |
3 | 真 | |
3 | 真 |
在考虑如何自动执行此问题时,我想我可以使用带有嵌套 Select Case 的 For 循环,然后是嵌套的 For 循环(使用 Step -1)。
第一个 For 循环从顶部开始,向下查看最后一行,在每一行中查看 BoM 级别,并根据此值使用 Select Case 运行 For 循环(步骤 -1)以开始向上查找行。
我能看到的问题是,我需要为每个可能的 BoM 级别创建一个案例(最大可能的总级别为 99)
到目前为止的代码摘录(仅转到案例 3):
Sub Purch_Child_Formula() 'ADD CHILD OF PURCHASED ASSY FORMULA
Dim AssyStatus As Double
Dim ChildPurchStatus As Double
Dim CurrentBomLevel As Integer
Dim X As Long
Last_Row = ActiveSheet.Range("A" & rows.Count).End(xlUp).Row
For ChildPurchStatus = 13 To Last_Row
CurrentBomLevel = Cells(ChildPurchStatus, 1).Value
Select Case CurrentBomLevel
Case 1
For X = ChildPurchStatus To 13 Step -1
If Cells(X - 1, 1).Value = CurrentBomLevel Then
Exit For
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 1) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
Else
Cells(X, 3).Value = "FALSE"
End If
Next X
Case 2
For X = ChildPurchStatus To 13 Step -1
If Cells(X - 1, 1).Value = CurrentBomLevel Then
Exit For
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 1) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 2) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
Else
Cells(X, 3).Value = "FALSE"
End If
Next X
Case 3
For X = ChildPurchStatus To 13 Step -1
If Cells(X - 1, 1).Value = CurrentBomLevel Then
Exit For
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 1) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 2) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
ElseIf Cells(X - 1, 1).Value = (CurrentBomLevel - 3) And Cells(X - 1, 2).Value = "P" Then
Cells(X, 3).Value = "TRUE"
Else
Cells(X, 3).Value = "FALSE"
End If
Next X
Case 4
Case 5
Case 6
Case 7
Case 8
Case 9
Case 10
End Select
Next ChildPurchStatus
End Sub
这开始起作用,但在达到 BoM 级别 4 之前确实出错了。
所以现在我在想,也许我应该使用一个首字母 for 循环来向下行,然后是另一个 for 循环来查找数据,但这个 for 循环应该继续,直到它在第 2 列中找到条件“P”——我们已经到达了一个 Until 循环。这可能吗?(很明显,编码不是我正常工作的一部分!
对不起,这篇非常冗长的帖子,但我的情况似乎是独一无二的,因为到目前为止,一些在线搜索并没有帮助。
希望这篇文章中有足够的信息。我将添加要求的任何其他详细信息。
答:
- 使用 object 存储上层 BoM 的状态。
Dictionary
Option Explicit
Sub Demo()
Dim i As Long, j As Long
Dim Sht1 As Worksheet
Dim arrRes, rngRes As Range
Dim objDic As Object, sKey As Long
Set Sht1 = Sheets("Sheet1") ' Modify as needed
Set rngRes = Sht1.Range("A1").CurrentRegion ' Modify as needed
arrRes = rngRes.Value
Set objDic = CreateObject("scripting.dictionary")
' Loop through data
For i = LBound(arrRes) To UBound(arrRes)
sKey = CLng(arrRes(i, 1))
If Not objDic.exists(sKey) Or UCase(arrRes(i, 2)) = "P" Then
objDic(sKey) = UCase(arrRes(i, 2))
End If
If sKey > 0 Then
arrRes(i, 3) = False
' Loop through up BoM
For j = sKey - 1 To 0 Step -1
If objDic(j) = "P" Then
arrRes(i, 3) = True
Exit For
End If
Next
End If
Next i
' Update data on sheet
rngRes.Value = arrRes
End Sub
你可以用一个公式来做到这一点。
=COUNTIFS(RC3:OFFSET(RC1,MAX(FILTER(ROW(R1C1:RC1),R1C1:RC1=0))-ROW(RC),2),"=P")>0
在 A1 模式下,我的单元格有这个:D1
=COUNTIFS($C1:OFFSET($A1,MAX(FILTER(ROW($A$1:$A1),$A$1:$A1=0))-ROW(D1),2),"=P")>0
评论
布尔解决方案
如前所述,我使用布尔值并跟踪哪个 BoM lvl 最低,我还有一个额外的变量:
Sub Purch_Child_Formula() 'ADD CHILD OF PURCHASED ASSY FORMULA
Dim lRow As Long, i As Long, bomLvl As Long
Dim ws As Worksheet
Dim pCheck As Boolean
Set ws = ActiveSheet
lRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
If lRow < 13 Then
MsgBox "Not enough rows to check"
Exit Sub
End If
Dim arr, arrPr
arr = ws.Range("A13:B" & lRow).Value 'from your example
ReDim arrPr(1 To UBound(arr, 1), 1 To 1)
For i = 1 To UBound(arr, 1)
If arr(i, 1) = 0 Then 'resetting
pCheck = False
bomLvl = 0
End If
If arr(i, 2) = "P" Then
If Not pCheck Then
pCheck = True
bomLvl = arr(i, 1)
End If
If bomLvl > arr(i, 1) Then bomLvl = arr(i, 1)
End If
arrPr(i, 1) = pCheck And bomLvl < arr(i, 1)
'there is a P and its BoM is lower than current one ... or not
Next i
ws.Range("C13:C" & lRow).Value = arrPr 'print everything at once
End Sub
如果以下结果错误,请告诉我:
评论