How to store array's values in lowercase? - excel

I read data I stored in a worksheet table into a VBA array. I need the first "column" of this array to be in lowercase. (The purpose of this array is to allow quicker calculations with the data contained in the table without referring to the table itself.)
I do this with the "LCase()" function, and I used the "Debug.print()" function to verify that they are being stored as lowercase.
However, later on in the code when I refer to this array, the values have reverted to their original case. I haven't added/edited the array beyond the point that I read the table data into it.
Simplified code:
Dim wb as Workbook
Dim ws as Worksheet
Dim tbl_Data as ListObject
Dim arr(1 to 10, 1 to 2) as Variant
Dim i as Integer
Dim calcValue as Single
Dim stringMatch as String
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet 1")
Set tbl_Data = ws.ListObjects("Table1")
For i = 1 to tbl_Data.ListRows.Count
arr(i, 1) = LCase(tbl_Data.DataBodyRange(i, 1))
arr(i, 2) = tbl_Data.DataBodyRange(i, 2))
Debug.Print(arr(i, 1)) 'Returns lowercase values normally
Next i
'---Insert calculations here
'- Returns calcValue (ex. calcValue = 10.12)
For i = 1 to UBound(arr, 1)
If calcValue = arr(i, 2) Then
Debug.Print(arr(i, 1)) 'Returns strings with original case values
stringMatch = arr(i, 1)
End If
Next i
I do not see an issue with the code that would cause the values stored to revert back to the original.
The original code. I hope that it makes sense, without the context of the data itself.
Option Explicit
Sub updateInventory()
Dim wb As Workbook
Dim sh_NewInventory As Worksheet
Dim sh_MasterInventory As Worksheet
Dim tbl_MasterInventory As ListObject
Dim cell_NewIngredient As Range
Dim arr_NewIngredients(1 To 30, 1 To 4) As Variant
Dim arr_MasterInventory(1 To 60, 1 To 6) As Variant
Dim i As Integer, j As Integer
Dim bool_isCellBlank As Boolean
Dim bool_isIngredientMatch As Boolean
Set wb = ThisWorkbook
Set sh_NewInventory = wb.Worksheets("Update Inventory")
Set sh_MasterInventory = wb.Worksheets("Food Inventory")
Set tbl_MasterInventory = sh_MasterInventory.ListObjects("MasterInventory")
Set cell_NewIngredient = sh_NewInventory.Range("B3")
bool_isCellBlank = False
bool_isIngredientMatch = False
i = 1
Do While Not bool_isCellBlank
arr_NewIngredients(i, 1) = LCase(cell_NewIngredient)
arr_NewIngredients(i, 2) = LCase(cell_NewIngredient.Offset(0, 1))
arr_NewIngredients(i, 3) = cell_NewIngredient.Offset(0, 2)
arr_NewIngredients(i, 4) = cell_NewIngredient.Offset(0, 3)
i = i + 1
Set cell_NewIngredient = cell_NewIngredient.Offset(1, 0)
bool_isCellBlank = (cell_NewIngredient = "")
Loop
For i = 1 To tbl_MasterInventory.ListRows.Count
arr_MasterInventory(i, 1) = LCase(tbl_MasterInventory.DataBodyRange(i, 1))
arr_MasterInventory(i, 2) = LCase(tbl_MasterInventory.DataBodyRange(i, 2))
For j = 1 To tbl_MasterInventory.ListColumns.Count - 2
arr_MasterInventory(i, j) = tbl_MasterInventory.DataBodyRange(i, j)
Next j
Next i
For i = 1 To UBound(arr_NewIngredients, 1)
j = 0
bool_isIngredientMatch = False
Do While Not bool_isIngredientMatch
j = j + 1
If arr_NewIngredients(i, 1) = LCase(arr_MasterInventory(j, 1)) Then
bool_isIngredientMatch = True
Debug.Print (arr_NewIngredients(i, 1) & " : " & arr_MasterInventory(j, 1))
End If
Loop
Next i
End Sub
RESULTS: Immediate Window

I figured it out!
See the following code (reading data into the array):
For i = 1 To tbl_MasterInventory.ListRows.Count
arr_MasterInventory(i, 1) = LCase(tbl_MasterInventory.DataBodyRange(i, 1))
arr_MasterInventory(i, 2) = LCase(tbl_MasterInventory.DataBodyRange(i, 2))
For j = 1 To tbl_MasterInventory.ListColumns.Count - 2
arr_MasterInventory(i, j) = tbl_MasterInventory.DataBodyRange(i, j)
Next j
Next i
I made a mistake in the loop using the "j" index. By starting at "j=1", I was replacing what I had done prior to the "j" For loop, which was what caused the data to be re-entered as the original version.
I feel real dumb for making the mistake, but I'm glad y'all looked at it for me! Thanks again!

Related

Getting error in vba subscript out of range for array and for loop

I have the follow code to fill cells in excel one by one and it works the way I want it to but it gives me this error when it runs through the array. How do I fix this error? Thanks
The error is "Subscript out of range. Error: 9"
Dim item As Variant
Dim splitString() As String
Dim finalSplit() As String
i = 1
For Each item In splitString
finalSplit = Split(splitString(s), ",")
Cells(i, 1) = finalSplit(0)
Cells(i, 2) = finalSplit(1)
Cells(i, 3) = finalSplit(2)
Cells(i, 4) = finalSplit(3)
i = i + 1
s = s + 1
Next
I checked if finalSplit contains enough values like Thomas said and it worked.This is the new code below.
Dim item As Variant
Dim splitString() As String
Dim finalSplit() As String
i = 1
For Each item In splitString
finalSplit = Split(splitString(s), ",")
If UBound(finalSplit) > 1 Then
Cells(i, 1) = finalSplit(0)
Cells(i, 2) = finalSplit(1)
Cells(i, 3) = finalSplit(2)
Cells(i, 4) = finalSplit(3)
i = i + 1
s = s + 1
End If
Next
As other commenters have pointed out, why not add another control variable?
Dim item As Variant
Dim splitString() As String
Dim finalSplit() As String
Dim i As Integer, j As Integer, s As Integer
i = 1
For Each item In splitString
finalSplit = Split(splitString(s), ",")
For j = 0 To UBound(finalSplit)
Cells(i, j + 1) = finalSplit(j)
Next j
i = i + 1
s = s + 1
Next
Be aware that this can loop more than the 4 times you expect. A lazy way to solve this would be to add If j > 3 Then Exit For before Next j
I tested this with the following code (it works!), as I have no idea what splitString() or finalSplit() is in your case:
Sub test()
Dim finalSplit As Variant
Dim j As Integer
finalSplit = Split("1,2,3,4,5", ",")
For j = 0 To UBound(finalSplit)
Cells(1, j + 1) = finalSplit(j)
If j > 3 Then Exit For
Next j
End Sub
Looping Through Elements of Arrays
An array created by the Split function is always 0-based (even if Option Base 1). Similarly, not quite related, an array created by the Array function is dependent on Option Base unless you use its parent VBA e.g. arr = VBA.Array(1,2,3). Then it is always zero-based.
Looping through the elements of an array (1D array) is done in the following two ways:
For Each...Next
Dim Item As Variant
For Each Item In Arr
Debug.Print Item
Next Item
For...Next
Dim i As Long
For i = LBound(Arr) To Ubound(Arr)
Debug.Print Arr(i)
Next i
Since we have established that Split always produces a zero-based array, in the second example we could use 0 instead of LBound(Arr):
`For...Next`
Dim i As Long
For i = 0 To Ubound(Arr)
Debug.Print Arr(i)
Next i
Option Explicit
Sub DoubleSplit()
Const IniString As String = "A,B,C,D/E,F,G,H/I,J,K/L/M,N,O,P,Q,R"
Dim SplitString() As String: SplitString = Split(IniString, "/")
Dim ws As Worksheet: Set ws = ActiveSheet ' be more specific
ws.Cells.ClearContents ' remove previous data; clears the whole worksheet
Dim FinalSplit() As String
Dim Item As Variant ' SplitString Control Variable
Dim r As Long ' Worksheet Row Counter
Dim f As Long ' FinalSplit Element Counter
' For Each...Next
For Each Item In SplitString
r = r + 1
FinalSplit = Split(Item, ",")
Debug.Print Join(FinalSplit, ",")
For f = 0 To UBound(FinalSplit)
ws.Cells(r, f + 1).Value = FinalSplit(f)
Next f
Next Item
r = r + 1 ' add an empty row
Dim s As Long ' SplitString Element Counter
' For...Next
For s = 0 To UBound(SplitString)
r = r + 1
FinalSplit = Split(SplitString(s), ",")
Debug.Print Join(FinalSplit, ",")
For f = 0 To UBound(FinalSplit)
ws.Cells(r, f + 1).Value = FinalSplit(f)
Next f
Next s
' Results
' A,B,C,D
' E,F,G,H
' I,J,K
' L
' M,N,O,P,Q,R
'
' A,B,C,D
' E,F,G,H
' I,J,K
' L
' M,N,O,P,Q,R
End Sub

Excel vba for each range and split problem

Please help to solve my problem.
I use below script but result nothing on activesheet..
Sub Dutylist()
Dim dutyTable(1 To 2, 1 To 4) As String
Dim Cyc As Integer, Team As Integer
Dim Svalue As Range, Srange As Range
Dim Result() As String
//Cycle 1
dutyTable(1, 1) = "A,B,C,D,E,F,G"
dutyTable(1, 2) = "D,C,A,B,A,E,D"
dutyTable(1, 3) = "B,A,E,C,B,D,E"
dutyTable(1, 4) = "C,B,C,D,C,A,A"
//Cycle 2
dutyTable(2, 1) = "B,E,D,A,D,B,A"
dutyTable(2, 2) = "B,A,E,C,C,D,B"
dutyTable(2, 3) = "D,C,A,B,B,E,B"
dutyTable(2, 4) = "E,A,B,D,A,C,D"
Cyc = 1
Set Srange = ActiveSheet.Range("a:a")
For Team = 1 To 4
Result = Split(dutyTable(Cyc, Team), ",")
For Each Svalue In Srange
If Svalue = "Team " & CStr(Team) Then
Svalue.Offset(0, 1).Resize(, UBound(Result) + 1).Value = Result
End If
Next Svalue
Next Team
End Sub
Any problem of my code above?
My understanding for your code execution should be as following, what I have changed is rearrange the Looping with some modification and also you should set range by reference to last used row instead of A:A to speed up the execution.
Basically the code will match the value on Col A for the Team, if found then return dutyTable1 value based on the cycle give by you,
Sub Dutylist()
Dim dutyTable(1 To 2, 1 To 4) As String
Dim Cyc As Integer, Team As Integer
Dim Svalue As Range, Srange As Range
Dim Result() As String
Dim i As Long
dutyTable(1, 1) = "A,B,C,D,E,F,G"
dutyTable(1, 2) = "D,C,A,B,A,E,D"
dutyTable(1, 3) = "B,A,E,C,B,D,E"
dutyTable(1, 4) = "C,B,C,D,C,A,A"
dutyTable(2, 1) = "B,E,D,A,D,B,A"
dutyTable(2, 2) = "B,A,E,C,C,D,B"
dutyTable(2, 3) = "D,C,A,B,B,E,B"
dutyTable(2, 4) = "E,A,B,D,A,C,D"
Cyc = 1
Set Srange = ActiveSheet.Range("a1:a20")
For Each Svalue In Srange
For Team = 1 To 4
Result = Split(dutyTable(Cyc, Team), ",")
If InStr(Svalue.Value, Team) > 0 Then
Svalue.Offset(0, 1).Resize(, UBound(Result) + 1).Value = Result
End If
Next Team
Next Svalue
End Sub
Output (Only 3 Team are tested, can be extended)

Coping Data from One Workbook To Another Based On Cell Data

I am trying to copy data from one workbook to another based on the values contained in cells in the source workbook that matches the same values in the target workbook. For example, I have a table (Table1) that has four columns say, A1:D5. One of these columns (column A) contains account numbers that match similar account numbers located on another workbook (also in column A). I am trying to find a code that looks through the table (Table1) in the source workbook via the account number column, and if the account number matches the account number in the target workbook, copy and paste the cells on that row in specific locations to the target workbook. Is this possible?
I hope that makes sense. I have looked all over on how to structure such a code, and I was not able to find anything to start the process for this logic.
Any help will be very appreciative.
Thank you
Even if your question is about doing this in VBA, I'm just going to mention that what you are trying to do seems like it could also be done with Power Query.
That being said, if you were to use VBA for this, you would have to use the Match function to find where your rows match and then copy the data from the source to the destination table.
I've adapted the code I provided to this question to better serve your specific needs. One of the things I've done is to add an optional argument called DoOverwrite and set it to false. This will make sure that the information from one row won't be overwritten by another row later down the road.
Sub TableJoinTest()
'Those table columns will have to match for the 2 lines to be a match
Dim MandatoryHeaders() As Variant
MandatoryHeaders = Array("Account Number")
Dim SourceTableAnchor As Range
Set SourceTableAnchor = Workbooks("SourceWorkbook.xlsx").Sheets("Sheet1").Range("A1")
Dim TargetTableAnchor As Range
Set TargetTableAnchor = Workbooks("TargetWorkbook.xlsx").Sheets("Sheet1").Range("A1")
TableJoin _
SourceTableAnchor:=SourceTableAnchor, _
TargetTableAnchor:=TargetTableAnchor, _
MandatoryHeaders:=MandatoryHeaders, _
AddIfMissing:=False, _
IsLogging:=False, _
DoOverwrite:=False
End Sub
Sub TableJoin( _
SourceTableAnchor As Range, _
TargetTableAnchor As Range, _
MandatoryHeaders As Variant, _
Optional OtherHeaders As Variant, _
Optional AddIfMissing As Boolean = False, _
Optional IsLogging As Boolean = False, _
Optional DoOverwrite As Boolean = True)
'''''''''''''''''''''''''''''''''''''''
'Definitions
'''''''''''''''''''''''''''''''''''''''
Dim srng As Range, trng As Range
Set srng = SourceTableAnchor.CurrentRegion
Set trng = TargetTableAnchor.CurrentRegion
Dim sHeaders As Range, tHeaders As Range
Set sHeaders = srng.Rows(1)
Set tHeaders = trng.Rows(1)
'Store in Arrays
Dim sArray() As Variant 'prefix s is for Source
sArray = ExcludeRows(srng, 1).Value2
Dim tArray() As Variant 'prefix t is for Target
tArray = ExcludeRows(trng, 1).Value2
Dim sArrayHeader As Variant
sArrayHeader = sHeaders.Value2
Dim tArrayHeader As Variant
tArrayHeader = tHeaders.Value2
'Find Column correspondance
Dim sMandatoryHeadersColumn As Variant
ReDim sMandatoryHeadersColumn(LBound(MandatoryHeaders) To UBound(MandatoryHeaders))
Dim tMandatoryHeadersColumn As Variant
ReDim tMandatoryHeadersColumn(LBound(MandatoryHeaders) To UBound(MandatoryHeaders))
Dim k As Long
For k = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
sMandatoryHeadersColumn(k) = Application.Match(MandatoryHeaders(k), sArrayHeader, 0)
tMandatoryHeadersColumn(k) = Application.Match(MandatoryHeaders(k), tArrayHeader, 0)
Next k
Dim sOtherHeadersColumn As Variant
ReDim sOtherHeadersColumn(LBound(OtherHeaders) To UBound(OtherHeaders))
Dim tOtherHeadersColumn As Variant
ReDim tOtherHeadersColumn(LBound(OtherHeaders) To UBound(OtherHeaders))
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
sOtherHeadersColumn(k) = Application.Match(OtherHeaders(k), sArrayHeader, 0)
tOtherHeadersColumn(k) = Application.Match(OtherHeaders(k), tArrayHeader, 0)
Next k
'Merge mandatory headers into one column (aka the helper column method)
Dim i As Long, j As Long
Dim sHelperColumn() As Variant
ReDim sHelperColumn(LBound(sArray, 1) To UBound(sArray, 1), 1 To 1)
For i = LBound(sArray, 1) To UBound(sArray, 1)
For j = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
sHelperColumn(i, 1) = sHelperColumn(i, 1) & sArray(i, sMandatoryHeadersColumn(j))
Next j
Next i
Dim tHelperColumn() As Variant
ReDim tHelperColumn(LBound(tArray, 1) To UBound(tArray, 1), 1 To 1)
For i = LBound(tArray, 1) To UBound(tArray, 1)
For j = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
tHelperColumn(i, 1) = tHelperColumn(i, 1) & tArray(i, tMandatoryHeadersColumn(j))
Next j
Next i
'Find all matches
Dim MatchList() As Variant
Dim LoggingColumn() As String
ReDim LoggingColumn(LBound(tArray, 1) To UBound(tArray, 1), 1 To 1)
For i = LBound(sArray, 1) To UBound(sArray, 1)
ReDim MatchList(LBound(tArray, 1) To UBound(tArray, 1))
For j = LBound(tArray, 1) To UBound(tArray, 1)
If sHelperColumn(i, 1) = tHelperColumn(j, 1) Then
MatchList(j) = 1
End If
Next j
'Get the row number for the match
Dim MatchRow As Long
Select Case Application.Sum(MatchList)
Case Is > 1
'Need to do more matching
Dim MatchingScoresList() As Long
ReDim MatchingScoresList(1 To UBound(tArray, 1))
Dim m As Long
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
For m = LBound(tArray, 1) To UBound(tArray, 1)
If tArray(m, sOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k)) Then
MatchingScoresList(m) = MatchingScoresList(m) + 2 ^ (UBound(OtherHeaders) - k)
End If
Next m
Next k
'Get the max score position
Dim MyMax As Long
MyMax = Application.Max(MatchingScoresList)
If Application.Count(Application.Match(MatchingScoresList(), Array(MyMax), 0)) > 1 Then
MsgBox "Error: can't determine how to match row " & i & " in source table"
Exit Sub
Else
MatchRow = Application.Match(MyMax, MatchingScoresList, 0)
End If
Case Is = 1
MatchRow = Application.Match(1, MatchList, 0)
Case Else
Dim nArray() As Variant, Counter As Long
If AddIfMissing Then
MatchRow = 0
Counter = Counter + 1
ReDim nArray(1 To Counter, 1 To UBound(tArray, 2))
For k = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
nArray(Counter, tMandatoryHeadersColumn(k)) = sArray(i, sMandatoryHeadersColumn(k))
Next k
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
nArray(Counter, tOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k))
Next k
Else
MsgBox "Error: Couldn't find a match for data row #" & i
Exit Sub
End If
End Select
'Logging and assigning values
If MatchRow > 0 Then
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
If tArray(MatchRow, tOtherHeadersColumn(k)) <> sArray(i, sOtherHeadersColumn(k)) Then
'Logging
If IsLogging And DoOverwrite Then LoggingColumn(MatchRow, 1) = LoggingColumn(MatchRow, 1) & _
IIf(LoggingColumn(MatchRow, 1) <> "", ", ", "") & _
tHeaders.Cells(1, tOtherHeadersColumn(k)) & " : " & _
tArray(MatchRow, tOtherHeadersColumn(k)) & _
" -> " & sArray(i, sOtherHeadersColumn(k))
'Assign new value
If DoOverwrite Or tArray(MatchRow, tOtherHeadersColumn(k)) = VbNullString Then
tArray(MatchRow, tOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k))
End If
End If
Next k
End If
Next i
'Write arrays to sheet
ExcludeRows(trng, 1).Value2 = tArray
With trng.Parent
If IsArrayInitialised(nArray) And AddIfMissing Then
.Cells(trng.Cells(1, 1).Row + trng.Rows.Count, trng.Cells(1, 1).Column).Resize(UBound(nArray, 1), UBound(nArray, 2)).Value2 = nArray
End If
If IsLogging Then
.Cells(trng.Cells(1, 1).Row, trng.Cells(1, 1).Column + trng.Columns.Count) = "Changes"
.Cells(trng.Cells(2, 1).Row, trng.Cells(1, 1).Column + trng.Columns.Count).Resize(UBound(LoggingColumn, 1), 1).Value2 = LoggingColumn
End If
End With
End Sub
And also add these functions inside your VBA project to as they are used in the procedure above.
Function IsArrayInitialised(ByRef A() As Variant) As Boolean
On Error Resume Next
IsArrayInitialised = IsNumeric(UBound(A))
On Error GoTo 0
End Function
Function ExcludeRows(MyRng As Range, StartRow As Long, Optional EndRow As Long = -1) As Range
'PURPOSE: Exclude one or more consecutives rows from an existing range
Dim Afterpart As Range, BeforePart As Range
If StartRow < 1 Or EndRow > MyRng.Rows.Count Then Set ExcludeRows = Nothing
If StartRow = 1 And EndRow = MyRng.Rows.Count Then Set ExcludeRows = Nothing
If EndRow = -1 Then EndRow = StartRow
If EndRow < MyRng.Rows.Count Then
With MyRng.Parent
Set Afterpart = .Range(MyRng.Cells(EndRow + 1, 1), MyRng.Cells(MyRng.Rows.Count, MyRng.Columns.Count))
End With
End If
If StartRow > 1 Then
With MyRng.Parent
Set BeforePart = .Range(MyRng.Cells(1, MyRng.Cells(1, 1).Column), MyRng.Cells(StartRow - 1, MyRng.Columns.Count))
End With
End If
Set ExcludeRows = Union2(True, BeforePart, Afterpart)
End Function
Public Function Union2(IgnoreEmptyRange As Boolean, ParamArray RangeArray() As Variant) As Range
'PURPOSE: Samae as Application.Union but allows some range object to be Empty
Dim V As Variant
Dim Rng As Range
For Each V In RangeArray
Do
If VarType(V) = vbEmpty Then Exit Do
Set Rng = V
If Not Union2 Is Nothing Then
Set Union2 = Union(Union2, Rng)
ElseIf Not Rng Is Nothing Then
Set Union2 = Rng
End If
Loop While False
Next
End Function

Code to output arrays to Excel spreadsheet is affecting prior iterations

I'm attempting to print small arrays to an Excel spreadsheet. The bulk of the code to loops n times based on the user's discretion.
The output Sub functions print correctly on the first iteration, but when the array changes on the next iteration and the sub functions move to the next line to output, they also modify the first array values in the spreadsheet from the first iteration.
Example: If I go through five iterations, and they all produce different values in their respective arrays, by the 5th iteration All five columns that have been printed are modified to be the same as the last iteration.
I'm trying to prevent the code from replacing previous values.
I've attempted the Erase function for the array inside of the big for loop, which broke the code.
For loop for iterations
Dim Iter As Integer
For Iter = 1 To number_of_iterations
Randomize
reward_present = Int((1 - 0 + 1) * Rnd + 0)
reward_string = reward_present
reward_present = 1
'Randomize whether there is a reward present or not
If reward_present = 1 Then
Dim door_probabilities() As Variant
ReDim door_probabilities(1 To number_of_doors)
Dim remainder As Double
Dim reward_door As Integer
Dim reward_door_string As String
remainder = 1
For i = 1 To (number_of_doors - 1)
door_probabilities(i) = RndDbl(0, remainder)
remainder = remainder - door_probabilities(i)
Next i
door_probabilities(number_of_doors) = remainder
'randomizing probabilities of each door
Dim max As Variant
max = door_probabilities(1)
reward_door = 0
For i = 1 To number_of_doors
If max <= door_probabilities(i) Then
max = door_probabilities(i)
reward_door = i
End If
Next i
reward_door_string = reward_door
'choosing the reward door based on probability
If number_of_doors = 3 Then
random_player_choice = Int((number_of_doors - 1 + 1) * Rnd + 1)
game_doors(random_player_choice) = 1
ArrayFillPlayer1 game_doors, Iter
'choose first player door randomly
'output here
For i = LBound(game_doors) To UBound(game_doors)
msg = msg & game_doors(i) & vbNewLine
Next i
MsgBox "Game doors player choice 1: " + msg
random_host_choice = Int((number_of_doors - 1 + 1) * Rnd + 1)
Do While random_host_choice = random_player_choice
random_host_choice = Int((number_of_doors - 1 + 1) * Rnd + 1)
Loop
If random_host_choice = reward_door Then
Do While random_host_choice = reward_door
random_host_choice = Int((number_of_doors - 1 + 1) * Rnd + 1)
Loop
End If
game_doors(random_host_choice) = 1
ArrayFillHost game_doors, Iter
'choose host door randomly
'output here
For i = LBound(game_doors) To UBound(game_doors)
msg = msg & game_doors(i) & vbNewLine
Next i
MsgBox "Game doors host choice: " + msg
random_player_choice2 = Int((number_of_doors - 1 + 1) * Rnd + 1)
Do While random_player_choice2 = random_host_choice
random_player_choice2 = Int((number_of_doors - 1 + 1) * Rnd + 1)
Loop
game_doors(random_player_choice2) = 1
'choose second player door
ArrayFillPlayer2 game_doors, Iter
For i = LBound(game_doors) To UBound(game_doors)
msg = msg & game_doors(i) & vbNewLine
Next i
ReDim game_doors(1 To number_of_doors)
End If
Sub ArrayFillPlayer1(TempArray As Variant, RowToWrite As Integer)
'Fill a range by transferring an array
Dim CellsDown As Long, CellsAcross As Long
Dim TheRange As Range
CellsDown = 1
CellsAcross = 3
'Cells.Clear
'Set worksheet range
Set TheRange = Range(Cells(RowToWrite, 1), Cells(CellsDown, CellsAcross))
Transfer temporary array to worksheet
TheRange.Value = TempArray
End Sub
Sub ArrayFillHost(TempArray As Variant, RowToWrite As Integer)
'Fill a range by transferring an array
Dim CellsDown As Long, CellsAcross As Long
Dim TheRange As Range
CellsDown = 1
CellsAcross = 6
'Cells.Clear
'Set worksheet range
Set TheRange = Range(Cells(RowToWrite, 4), Cells(CellsDown, CellsAcross))
Transfer temporary array to worksheet
TheRange.Value = TempArray
End Sub
Sub ArrayFillPlayer2(TempArray As Variant, RowToWrite As Integer)
'Fill a range by transferring an array
Dim CellsDown As Long, CellsAcross As Long
Dim TheRange As Range
CellsDown = 1
CellsAcross = 9
'Cells.Clear
'Set worksheet range
Set TheRange = Range(Cells(RowToWrite, 7), Cells(CellsDown, CellsAcross))
Transfer temporary array to worksheet
TheRange.Value = TempArray
End Sub
I expect the output of each consecutive row to be different, but they are all modified retroactively.
Kind of looks like you mean to use Resize() in your subs which fill the array to the sheet?
For example this:
Sub ArrayFillPlayer1(TempArray As Variant, RowToWrite As Integer)
'Fill a range by transferring an array
Dim CellsDown As Long, CellsAcross As Long
Dim TheRange As Range
CellsDown = 1
CellsAcross = 3
'Cells.Clear
'Set worksheet range
Set TheRange = Range(Cells(RowToWrite, 1), Cells(CellsDown, CellsAcross))
Transfer temporary array to worksheet
TheRange.Value = TempArray
End Sub
Here the line:
Set TheRange = Range(Cells(RowToWrite, 1), Cells(CellsDown, CellsAcross))
is the same as:
Set TheRange = Range( Cells(RowToWrite, 1), Range("C1") )
and that "C1" remains constant as RowToWrite increases, so each time you're filling a larger range with TempArray.
This is closer to what you want:
Sub ArrayFillPlayer1(TempArray As Variant, RowToWrite As Integer)
Range(Cells(RowToWrite, 1).Resize(1, 3).Value = TempArray
End Sub

Sum values from a specific month and user from a large CSV file with 4.5 million lines

I have a huge .csv file with over 4.5 million lines. As this is to big for Excel I need to search the .csv file for any entries from each user and then sum them but the sum needs to be done for a specific month.
Excel
USER Month total value
AAH Febuary 2010 1014
CSV
"USER","DATE_TIME","NUMBER"
"AAH","2010-03-18T17:35:01.000Z","410.0"
"ABH","2011-01-24T09:43:01.000Z","336.0"
"AAH","2010-03-18T19:25:01.000Z","114.0"
"BhC","2012-06-24T03:45:01.000Z","336.0"
"AAH","2010-03-20T19:30:01.000Z","490.0"
Can you help me with a solution ?
You can do it with (tweak to taste) the below. It works on your test data (duplicated to 5.5 million rows or around 230MB it takes about 30 secs on my laptop. No doubt, if performance is vital, it can be improved but it is probably sufficiently fast for your purposes).
Option Explicit
Sub GetData()
Dim fso As Object
Dim fs As Object
Dim results As Collection
Dim arr
Dim i As Long
Dim monthOfInterest As Integer
Dim recordMonth As Date
Dim recordUser As String
Dim recordValue As Variant
Dim recordKey As String
Dim result As Variant
Dim str As String, splitStr() As String
Dim ws As Worksheet
Set fso = CreateObject("Scripting.FileSystemObject")
Set results = New Collection
'enter your path here or use something like FileDialog
Set fs = fso.OpenTextFile("C:\test.csv", ForReading, False, TristateFalse) 'TristateTrue if Unicode
monthOfInterest = 3
If not fs.AtEndOfStream Then fs.SkipLine 'skip past headers at top of CSV
Do While Not fs.AtEndOfStream
splitStr = Split(fs.ReadLine, ",")
If fs.Line Mod 10000 = 0 Then
Application.StatusBar = "Line " & fs.Line
DoEvents
End If
recordMonth = DateSerial( _
Mid(splitStr(1), 2, 4), _
Mid(splitStr(1), 7, 2), 1)
If month(recordMonth) = monthOfInterest Then
recordUser = Mid(splitStr(0), 2, Len(splitStr(0)) - 2)
recordValue = CDec(Mid(splitStr(2), 2, Len(splitStr(2)) - 2))
recordKey = recordUser & "|" & Format(recordMonth, "YYYY-MM")
On Error Resume Next
result = results(recordKey)
If Err.Number <> 5 Then 'key exists
results.Remove recordKey
recordValue = recordValue + result(2)
End If
On Error GoTo 0
results.Add Array(recordUser, recordMonth, recordValue), recordKey
End If
Loop
fs.Close
Application.StatusBar = "Outputting..."
'Process results and dump to worksheet
If results.Count > 0 Then
Set ws = ActiveWorkbook.Worksheets.Add
ReDim arr(0 To results.Count, 0 To 2)
arr(0, 0) = "User"
arr(0, 1) = "Month"
arr(0, 2) = "Total"
For i = 1 To UBound(arr, 1)
arr(i, 0) = results(i)(0)
arr(i, 1) = results(i)(1)
arr(i, 2) = results(i)(2)
Next i
ws.Range(ws.Cells(1, 1), ws.Cells(1 + UBound(arr, 1), 1 + UBound(arr, 2))).Value = arr
End If
Application.StatusBar = ""
End Sub

Resources