我需要我的程序来识别类型是水果还是沙拉,以及它的形状是好是坏。 取决于它必须乘以 Prcies*Stock.
所以我做了这段代码,但是当它超过 5000 行时,它真的很慢。 我怎么能不使用 out loopin 来做到这一点?
Sub Go()
Set Page1 = Worksheets("Page 1")
LastRow = Page1.Cells(Page1.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
If Range("B" & i).Value = "fruit" And Range("C" & i).Value = "good" Then
Range("E" & i).Value = Range("D" & i) * Range("I2").Value
Else
If Range("B" & i).Value = "fruit" And Range("C" & i).Value = "bad" Then
Range("E" & i).Value = Range("D" & i).Value * Range("J2").Value
Else
If Range("B" & i).Value = "salad" And Range("C" & i).Value = "good" Then
Range("E" & i).Value = Range("D" & i) * Range("I3").Value
Else
If Range("B" & i).Value = "salad" And Range("C" & i).Value = "bad" Then
Range("E" & i).Value = Range("D" & i) * Range("J3").Value
Else
Range("E" & i).Value = "error"
End If
End If
End If
End If
Next
End Sub
使用字典
Sub CalcTotalsDict()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Page 1")
Dim srg As Range, rCount As Long
With ws.Range("A1").CurrentRegion
rCount = .Rows.Count - 1
Set srg = .Resize(rCount).Offset(1)
End With
Dim sData(): sData = srg.Value
Dim lPrices(), lTypes(), lStates(), lrCount As Long, lcCount As Long
With ws.Range("H1").CurrentRegion
lrCount = .Rows.Count - 1
lcCount = .Columns.Count - 1
lTypes = .Resize(lrCount, 1).Offset(1).Value
lStates = .Resize(1, lcCount).Offset(, 1).Value
lPrices = .Resize(lrCount, lcCount).Offset(1, 1).Value
End With
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim lr As Long, lc As Long, lStr As String
For lr = 1 To lrCount
For lc = 1 To lcCount
lStr = CStr(lTypes(lr, 1)) & "@" & CStr(lStates(1, lc))
If Not dict.Exists(lStr) Then
dict(lStr) = lPrices(lr, lc)
End If
Next lc
Next lr
Dim dData(): ReDim dData(1 To rCount, 1 To 1)
Dim r As Long, sStr As String
For r = 1 To rCount
sStr = CStr(sData(r, 2)) & "@" & CStr(sData(r, 3))
If dict.Exists(sStr) Then
dData(r, 1) = dict(sStr) * sData(r, 4)
Else
dData(r, 1) = "Error"
End If
Next r
srg.Columns(5).Value = dData
MsgBox "Totals calculated.", vbInformation
End Sub
使用 Application.Match
Sub CalcTotalsMatch()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Page 1")
Dim srg As Range, rCount As Long
With ws.Range("A1").CurrentRegion
rCount = .Rows.Count - 1
Set srg = .Resize(rCount).Offset(1)
End With
Dim sData(): sData = srg.Value
Dim lPrices(), lTypes(), lStates(), lrCount As Long, lcCount As Long
With ws.Range("H1").CurrentRegion
lrCount = .Rows.Count - 1
lcCount = .Columns.Count - 1
lTypes = .Resize(lrCount, 1).Offset(1).Value
lStates = .Resize(1, lcCount).Offset(, 1).Value
lPrices = .Resize(lrCount, lcCount).Offset(1, 1).Value
End With
Dim dData(): ReDim dData(1 To rCount, 1 To 1)
Dim lrIndex, lcIndex, r As Long, IsFound As Boolean
Dim sType As String, sState As String, sStock As Long
For r = 1 To rCount
sType = CStr(sData(r, 2))
lrIndex = Application.Match(sType, lTypes, 0)
If IsNumeric(lrIndex) Then
sState = CStr(sData(r, 3))
lcIndex = Application.Match(sState, lStates, 0)
If IsNumeric(lcIndex) Then
IsFound = True
End If
End If
If IsFound Then
sStock = sData(r, 4)
dData(r, 1) = lPrices(lrIndex, lcIndex) * sStock
IsFound = False
Else
dData(r, 1) = "Error"
End If
Next r
srg.Columns(5).Value = dData
MsgBox "Totals calculated.", vbInformation
End Sub
主要
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Calculates totals...
' Calls: GetJoinedColumnRanges
' GetSingleColumn
' GetSingleColumn
' DictJoinedTables
' GetMultipliedUniques
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub CalcTotals()
Const WS_NAME As String = "Page 1"
Const LKP_FIRST_CELL As String = "H1"
Const SRC_FIRST_CELL As String = "A1"
Dim sUniqueColumns(): sUniqueColumns = Array(2, 3)
Const SRC_COUNT_COLUMN As Long = 4
Const DST_COLUMN As Long = 5
Const UNIQUE_DELIMITER As String = "@"
Const NO_MATCH_STRING As String = "Error"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets(WS_NAME)
Dim rg As Range
With ws.Range(SRC_FIRST_CELL).CurrentRegion
Set rg = .Resize(.Rows.Count - 1).Offset(1)
End With
Dim suData():
suData = GetJoinedColumnRanges(rg, sUniqueColumns, UNIQUE_DELIMITER)
Dim scData(): scData = GetSingleColumn(rg, SRC_COUNT_COLUMN)
Dim lrg As Range: Set lrg = ws.Range(LKP_FIRST_CELL).CurrentRegion
Dim lDict As Object: Set lDict = DictJoinedTable(lrg, UNIQUE_DELIMITER)
Dim dData():
dData = GetMultipliedUniques(suData, scData, lDict, NO_MATCH_STRING)
rg.Columns(DST_COLUMN).Value = dData
MsgBox "Totals calculated.", vbInformation
End Sub
加入列范围
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the joined ('Delimiter') columns ('ColumnIndexes')
' of a range ('rg') in a 2D one-based single-column array.
' Calls: GetSingleColumn
' Date: 2023
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetJoinedColumnRanges( _
ByVal rg As Range, _
ByVal ColumnIndexes As Variant, _
Optional ByVal Delimiter As String = " ") _
As Variant
Dim LB As Long: LB = LBound(ColumnIndexes)
Dim sData(), dData(), n As Long, r As Long, rCount As Long
For n = LB To UBound(ColumnIndexes)
If n = LB Then
dData = GetSingleColumn(rg.Columns(ColumnIndexes(n)))
rCount = UBound(dData, 1)
Else
sData = GetSingleColumn(rg.Columns(ColumnIndexes(n)))
For r = 1 To rCount
dData(r, 1) = dData(r, 1) & Delimiter & CStr(sData(r, 1))
Next r
End If
Next n
GetJoinedColumnRanges = dData
End Function
获取单列
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a column ('ColumnIndex')
' of a range ('rg') in a 2D one-based single-column array.
' Date: 2023
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetSingleColumn( _
ByVal rg As Range, _
Optional ByVal ColumnIndex As Long = 1) _
As Variant
Dim Data()
With rg.Columns(ColumnIndex)
If rg.Rows.Count = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = .Value
Else
Data = .Value
End If
End With
GetSingleColumn = Data
End Function
连接表到字典
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a table with the first column of row labels and the first
' row of column labels, returns the joined label combinations
' in the keys, and each associated value in the items
' of a dictionary.
' Date: 2023
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function DictJoinedTable( _
ByVal rg As Range, _
Optional ByVal Delimiter As String = " ") _
As Object
Dim Data(): Data = rg.Value
Dim rCount As Long: rCount = rg.Rows.Count
Dim cCount As Long: cCount = rg.Columns.Count
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim r As Long, c As Long, kStr As String
For r = 2 To rCount
For c = 2 To cCount
kStr = Data(r, 1) & Delimiter & Data(1, c)
If Not dict.Exists(kStr) Then
dict(kStr) = Data(r, c)
End If
Next c
Next r
If dict.Count > 0 Then
Set DictJoinedTable = dict
End If
End Function
获得成倍的独特性
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Looks for each value of a 2D one-based array ('UniqueData')
' in the keys of a dictionary ('LookupDictionary') to return
' the product of the associated item and the value in another
' same-sized 2D one-based array ('MultiplyData') in the resulting
' same-sized 2D one-based array. If not found, instead
' of the product, a string ('NoMatchString') is returned.
' Date: 2023
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetMultipliedUniques( _
ByVal UniqueData As Variant, _
ByVal MultiplyData As Variant, _
ByVal LookupDictionary As Object, _
ByVal NoMatchString As String) _
As Variant
Dim rCount As Long: rCount = UBound(MultiplyData, 1)
Dim dData(): ReDim dData(1 To rCount, 1 To 1)
Dim r As Long, sStr As String
For r = 1 To rCount
sStr = UniqueData(r, 1)
If LookupDictionary.Exists(sStr) Then
dData(r, 1) = LookupDictionary(sStr) * MultiplyData(r, 1)
Else
dData(r, 1) = NoMatchString
End If
Next r
GetMultipliedUniques = dData
End Function
在我看来,您的问题实际上是关于性能的(“它真的很慢”)。因此,无论何时处理大范围,都可以轻松显着提高性能:
要让 Excel 在范围/工作表和 VBA 之间来回切换,需要大量的 Windows 开销。此过程在每个方向进行一次行程,因此开销很小。每当我处理多个细胞时,我都会使用这个过程。请注意,您不需要将
Dim
变体作为数组,因为变体可以容纳一个数组。但是您确实需要在写入之前Redim
输出数组,这样您就可以像在任何其他数组中一样处理单个元素。
我怀疑@BigBen 是对的,这可能只用工作表公式就可以完成,但我认为这个“范围到变体和回到范围”的过程值得理解。警告:当复制的输入范围是单个单元格时,变量是标量。在这种情况下尝试使用数组表示法访问它会产生错误。
这是一种方法:
Sub Calculate() ' Can't use Go() because Go is an Excel keyword
' It's good practice to Dim all variables
Dim Page1 As Worksheet, LastRow As Long, i As Long, vntContent As Variant, vntOutput As Variant
Dim vntPrices As Variant, nPriceRow As Integer, nPriceCol As Integer, rngData As Range
' Count the rows
Set Page1 = Worksheets("Sheet6")
LastRow = Page1.Cells(Page1.Rows.Count, "A").End(xlUp).Row
Set rngData = Page1.Cells(1, 8).Resize(3, 3) ' Price table
vntPrices = rngData
Set rngData = Page1.Cells(1, 1).Resize(LastRow, 4) ' Input range
vntContent = rngData
ReDim vntOutput(1 To LastRow, 1 To 1)
For i = 2 To LastRow
' Determine the price lookup column
Select Case vntContent(i, 3)
Case "good"
nPriceCol = 2
Case "bad"
nPriceCol = 3
Case Else
nPriceCol = 0
End Select
' Determine the price lookup row
Select Case vntContent(i, 2)
Case "fruit"
nPriceRow = 2
Case "salad"
nPriceRow = 3
Case Else
nPriceRow = 0
End Select
' If both are valid, calculate price * stock
If nPriceRow > 0 And nPriceCol > 0 Then
vntOutput(i, 1) = vntPrices(nPriceRow, nPriceCol) * vntContent(i, 4)
Else
vntOutput(i, 1) = "Error"
End If
Next i
' Write the output to the P*S column
Set rngData = Page1.Cells(1, 5).Resize(LastRow, 1)
rngData = vntOutput
End Sub