I'm creating an array from a text file and want to create a "subarray" from the main one.
The main array has the form
And I want to extract the A and B.
I create the "sub array" by splitting the strings from each row
For n = LBound(MainArray) To UBound(MainArray)
If Split(MainArray(n), " ")(0) = "Data" Then
ReDim SubArray(X)
SubArray(X) = Split(MainArray(n), " ")(1)
X = X + 1
End If
Next n
but doing this just returns the array (written as a vector now) (" ", B).
Why does A get overwritten by an empty space after the for loop finds the B?
Thanks and Happy Easter!
Note the example above is just a minimalist version of the real array.
This answer is predicated on Main array being a single dimension array.
The problem you are having is that you are nott creating new sub arrays each time tou get a new 'Data xxx" and consequently just keep overwriting the previous subarray.
You will be better served in you endeavour by using a dictionary of dictionaries.
To use dictionaries you either have to add a reference to the Microsoft Scripting Runtime or use 'CreateObject("Scripting.Dicitonary"). The first option is preferred when developing code or when you are a newbie because you get intellisense. You don't get intellisense when you use late bound objects (created by CreateObject).
Scripting.Dictionaries should be preferred over collections with keys because Dictionaries allow you to retreive the Keys or Items as arrays in their own right.
Here is your code modified to use scripting Dictionaries
Dim myD As Scripting.Dictionary
Set myD = New Scripting.Dictionary
Dim mySubDName As String
mySubDName = "Unknown"
Dim myItem As Variant
For Each myItem In MainArray
If InStr(myItem, "Data") > 0 Then
mySubDName = Trim(myItem)
If Not myD.exists(SubDName) Then
' Create a new sub dictionary with key 'Data XXXX'
myD.Add mySubDName, New Scripting.Dictionary
End If
Else
Dim myArray As Variant
myArray = Split(Trim(myItem), " ")
myD.Item(mySubDName).Add myArray(0), myArray(1)
End If
Next
Dictionary myD will have Keys of "Data A", Data B" etc.
You retrieve a sub dictionary using
'Where XXXX is A,B,C etc
set mySubD = myD.Item("Data XXXX")
The sub dictionary has the structure (using 00000007 700 as an example) of Key=00000007 and Item = 700
If you enumerate a Dictionary using for each it returns the Key as the control variable.
You can get an array of the Keys using the .Keys method
you can Get an array of the Items using the .Items Method
E.g.
myD.Keys gives the array ("Data A", "Data B", "Data C", ....."Data XXX"
myD.Item("Data B").Items will give the array ("0000005", "0000006",.....,"00000010, etc"
Please do take the ttime to read up on Scripting.Dictionaries as part of understanding the above.
Good luck with your coding.
Since you do not answer the clarification questions, please try the next code, which processes a 2D array, resulting two 2D arrays, corresponding to 'Data A' and 'Data B':
Sub Split2DArray()
Dim MainArray, arrA, arrB, n As Long, iA As Long, iB As Long, boolFirst As Boolean
'for exemplification place the picture content in A:A column, then place it in a (2D) array:
MainArray = Range("A1:A13").value
ReDim arrA(1 To 1, 1 To UBound(MainArray)): iA = 1
ReDim arrB(1 To 1, 1 To UBound(MainArray)): iB = 1
For n = LBound(MainArray) To UBound(MainArray)
If MainArray(n, 1) <> "" Then
If Split(MainArray(n, 1), " ")(0) = "Data" Then
If Not boolFirst Then
boolFirst = True
arrA(1, iA) = MainArray(n, 1): iA = iA + 1
Else
boolFirst = False
arrB(1, iB) = MainArray(n, 1): iB = iB + 1
End If
ElseIf boolFirst Then
arrA(1, iA) = MainArray(n, 1): iA = iA + 1
Else
arrB(1, iB) = MainArray(n, 1): iB = iB + 1
End If
End If
Next n
If iA > 1 Then ReDim Preserve arrA(1 To 1, 1 To iA - 1) 'only the second dimension can be preserved
If iB > 1 Then ReDim Preserve arrB(1 To 1, 1 To iB - 1)
Range("C1").Resize(UBound(arrA, 2), 1).value = Application.Transpose(arrA)
Range("D1").Resize(UBound(arrB, 2), 1).value = Application.Transpose(arrB)
End Sub
The code can be easily adapted to process 1D arrays. If this is the case I can show you how to proceed. If many such 'Data x' slices exist, you should use a Dictionary keeping each array.
The same processing way for 1D arrays. Using the same visual elocvent way of testing:
Sub Split1DArray()
Dim MainArray, arrA, arrB, n As Long, iA As Long, iB As Long, boolFirst As Boolean
'for exemplification place the picture content in A:A column, then place it in a (2D) array:
MainArray = Application.Transpose(Range("A1:A13").value) 'obtaining a 1D array from the same reange...
ReDim arrA(1 To UBound(MainArray)): iA = 1
ReDim arrB(1 To UBound(MainArray)): iB = 1
For n = LBound(MainArray) To UBound(MainArray)
If MainArray(n) <> "" Then
If Split(MainArray(n), " ")(0) = "Data" Then
If Not boolFirst Then
boolFirst = True
arrA(iA) = MainArray(n): iA = iA + 1
Else
boolFirst = False
arrB(iB) = MainArray(n): iB = iB + 1
End If
ElseIf boolFirst Then
arrA(iA) = MainArray(n): iA = iA + 1
Else
arrB(iB) = MainArray(n): iB = iB + 1
End If
End If
Next n
If iA > 1 Then ReDim Preserve arrA(1 To iA - 1) 'only the second dimension can be preserved
If iB > 1 Then ReDim Preserve arrB(1 To iB - 1)
Range("C1").Resize(UBound(arrA), 1).value = Application.Transpose(arrA)
Range("D1").Resize(UBound(arrB), 1).value = Application.Transpose(arrB)
End Sub
And a version using a dictionary, processing as many as `Data x' slices exist:
Sub Split1DArrayDict()
Dim MainArray, n As Long, x As Long, arrIt, dict As Object
'for exemplification place the picture content in A:A column, then place it in a (2D) array:
MainArray = Application.Transpose(Range("A1:A18").value) 'obtaining a 1D array from the same range...
Set dict = CreateObject("Scripting.Dictionary")
For n = LBound(MainArray) To UBound(MainArray)
If MainArray(n) <> "" Then
If Split(MainArray(n), " ")(0) = "Data" Then
x = x + 1
dict.Add x, Array(MainArray(n))
arrIt = dict(x)
Else
ReDim Preserve arrIt(UBound(arrIt) + 1)
arrIt(UBound(arrIt)) = MainArray(n)
dict(x) = arrIt
End If
End If
Next n
For n = 0 To dict.count - 1
cells(1, 3 + n).Resize(UBound(dict.items()(n)) + 1, 1).value = Application.Transpose(dict.items()(n))
Next n
End Sub
Related
I have 2 arrays that I want to combine into a single array of all possible combinations. I then need to loop through all of the combinations and popular arguments for a function. My arrays are not equal in size, and my attempts so far have resulted in a combined array only having 1 pair of values. This is VBA in PowerPoint, not Excel, if that makes a difference to available syntax.
How can I go from this:
arrayColor = Array("Blue","Green","Red")
arraySize = Array("XS","S","M","L","XL")
To this:
arrayCombo(0,0) = "Blue"
arrayCombo(0,1) = "XS"
arrayCombo(1,0) = "Blue"
arrayCombo(1,1) = "S"
...
arrayCombo(15,0) = "Red"
arrayCombo(15,1) = "XL"
And then use a loop to call each pair of values and populate argument values. This code just to illustrate the concept; it's certainly not legit. Pretty sure I need a nested loop here?
For i = 0 To UBound(arrayCombo(i))
nextSubToFire(color, size)
Next i
This is what I've got so far, but it only results in a single pair in my combined array. It's based on this question, but I think I'm either missing something or the sole answer there isn't quite correct. I've looked at other similar questions, but can't wrap my head around doing this with an array compiled in the code rather than the other examples all tailored to Excel.
Option Explicit
Dim arrayColorSize, arrayCombo
Sub CoreRoutine()
Dim arrayColor, arraySize
arrayColor = Array("Blue","Green","Red")
arraySize = Array("XS","S","M","L","XL")
arrayColorSize = Array(arrayColor, arraySize)
arrayCombo = Array(0, 0)
DoCombinations (0)
Dim a As Integer
Dim b As Integer
'For loop comes next once I figure out how to populate the full arrayCombo
End Sub
Sub DoCombinations(ia)
Dim i
For i = 0 To UBound(arrayColorSize(ia)) ' for each item
arrayCombo(ia) = arrayColorSize(ia)(i) ' add this item
If ia = UBound(arrayColorSize) Then
Else
DoCombinations (ia + 1)
End If
Next i
End Sub
Using the Locals window, I see arrayCombo exists, but it only has 1 pair of values in it, which is the last set of pairing options. I see that arrayColorSize has the 2 array sets as I'd expect, so I suspect the DoCombinations sub is missing something.
Any guidance much appreciated!
One way of doing this is to combine the two 1D arrays into a 2D array with 2 columns (as in your example):
Private Function Combine1DArrays(ByRef arr1 As Variant, ByRef arr2 As Variant) As Variant
If GetArrayDimsCount(arr1) <> 1 Or GetArrayDimsCount(arr2) <> 1 Then
Err.Raise 5, "Combine1DArrays", "Expected 1D arrays"
End If
'
Dim count1 As Long: count1 = UBound(arr1) - LBound(arr1) + 1
Dim count2 As Long: count2 = UBound(arr2) - LBound(arr2) + 1
Dim i As Long, j As Long, r As Long
Dim result() As Variant
'
ReDim result(0 To count1 * count2 - 1, 0 To 1)
r = 0
For i = LBound(arr1) To UBound(arr1)
For j = LBound(arr2) To UBound(arr2)
result(r, 0) = arr1(i)
result(r, 1) = arr2(j)
r = r + 1
Next j
Next i
Combine1DArrays = result
End Function
Public Function GetArrayDimsCount(ByRef arr As Variant) As Long
Const MAX_DIMENSION As Long = 60
Dim dimension As Long
Dim tempBound As Long
'
On Error GoTo FinalDimension
For dimension = 1 To MAX_DIMENSION
tempBound = LBound(arr, dimension)
Next dimension
FinalDimension:
GetArrayDimsCount = dimension - 1
End Function
You can use it like this for example:
Sub CoreRoutine()
Dim arrayColorSize As Variant
Dim i As Long
Dim color As String
Dim size As String
'
arrayColorSize = Combine1DArrays(Array("Blue", "Green", "Red") _
, Array("XS", "S", "M", "L", "XL"))
For i = LBound(arrayColorSize, 1) To UBound(arrayColorSize, 1)
color = arrayColorSize(i, 0)
size = arrayColorSize(i, 1)
NextSubToFire color, size
Next i
End Sub
Sub NextSubToFire(ByVal color As String, ByVal size As String)
Debug.Print color, size
End Sub
I have a giant dataset that looks like this
I am trying to go down the list of different companies and grab 3 per company and combine them. Based on the photo above, I would have 2 different lists with 3 companies each (except TH Repair which will have 2 in the final list).
My real dataset contains hundreds of different companies, each with dozens/hundreds of entries so I would finish with dozens of lists (each potentially hundreds long).
I tried to record a macro and ended up with this code
Sub Loop1()
'
' Loop1 Macro
'
'
Range("A4:E6").Select
Selection.Copy
Sheets("Sheet3").Select
Range("A18").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A11:E13").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A21").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A17:E19").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A24").Select
ActiveSheet.Paste
End Sub
However, this turned out to be WAY more complicated then I expected.
I am looking for the end result to look like this
See if something like this works for you. I only ran one scenario through it so you will want to test it more.
This makes the assumption that the data is sorted by column B on the original sheet
This procedure makes an assumption that there is either headers or no data on row 1.
You will need to change the "Sheet1" in this line Set ws1 = ActiveWorkbook.Worksheets("Sheet1") to the name of the sheet you are starting with.
Option Explicit
Public Sub MoveData()
Dim ws1 As Worksheet
Set ws1 = ActiveWorkbook.Worksheets("Sheet1")
Dim ws2 As Worksheet
Set ws2 = ActiveWorkbook.Worksheets.Add()
Dim rw As Long
Dim match_count As Integer
Dim list_multiplier As Integer
list_multiplier = 7
Dim list_row() As Long
ReDim list_row(0)
list_row(0) = 2
For rw = 2 To ws1.Range("A" & ws1.Rows.Count).End(xlUp).Row
If ws1.Range("B" & rw).Value <> ws1.Range("B" & rw).Offset(-1, 0).Value Then
match_count = 0
Else
match_count = match_count + 1
End If
Dim list_num As Integer
list_num = match_count \ 3
If list_num > UBound(list_row, 1) Then
ReDim Preserve list_row(list_num)
list_row(list_num) = 2
End If
ws2.Cells(list_row(list_num), 1 + list_multiplier * list_num).Value = ws1.Range("A" & rw).Value
ws2.Cells(list_row(list_num), 2 + list_multiplier * list_num).Value = ws1.Range("B" & rw).Value
ws2.Cells(list_row(list_num), 3 + list_multiplier * list_num).Value = ws1.Range("C" & rw).Value
ws2.Cells(list_row(list_num), 4 + list_multiplier * list_num).Value = ws1.Range("D" & rw).Value
ws2.Cells(list_row(list_num), 5 + list_multiplier * list_num).Value = ws1.Range("E" & rw).Value
list_row(list_num) = list_row(list_num) + 1
Next rw
End Sub
When you record your macro, ensure that "Use Relative References" on the Developer Ribbon tab is enabled, :)
assuming row 3 has your data headers, you could try this:
Option Explicit
Sub main()
Dim nLists As Long, iList As Long
Dim data As Variant
Dim dataToDelete As Range
With Range("F3", Cells(Rows.Count, 1).End(xlUp))
data = .Value
nLists = WorksheetFunction.Max(.Resize(,1))
nLists = nLists \ 3 + IIf(nLists - 3 * (nLists \ 3) = 0, -1, 0)
End With
With Range("A3").Resize(, 6)
For iList = 0 To nLists
Set dataToDelete = Nothing
With .Offset(, iList * 6).Resize(UBound(data))
.Value = data
.AutoFilter Field:=1, Criteria1:="<=" & iList * 3, Criteria2:=">" & (iList + 1) * 3, Operator:=xlOr
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then Set dataToDelete = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
.Parent.AutoFilterMode = False
If Not dataToDelete Is Nothing Then dataToDelete.Delete xlShiftUp
End With
Next
End With
End Sub
Your task is actually slightly trickier than your online advice suggests. Basically, you have to do the following:
Find out how many unique 'keys' (ie unique items in column B) you have. This will tell you the total number of rows you need (ie number of unique keys * 3)
Count the number of items for each 'key'. This will tell you how many columns you need (ie max item count / 3 * number of columns in array [A:E = 5])
Loop through each line of data and it put on appropriate row for that 'key'. Once three has been reached, jump the column for that key 6 columns to the right, and continue.
If you were to use a Class object and Collection type of object, this could be really quite concise code, but judging by your post you are at the beginning of your programming journey in VBA. Therefore, I've broken down each task into separate chunks of code so you will hopefully see how arrays can work for you. Once you practise with arrays a little, perhaps you could have a go at making this code more efficient by combining some of the loops:
Public Sub RunMe()
Dim data As Variant
Dim r As Long, c As Long, i As Long, dataRows As Long, dataCols As Long, keyLen As Long, maxCount As Long
Dim keys As String
Dim k As Variant
Dim keyArray() As String
Dim keyCount() As Long, threeCount() As Long, rowNum() As Long, colNum() As Long
Dim output() As Variant
'Read the data - change "Sheet1" to your sheet name.
'Shows how to write range values into a variant to
'create an array of variants.
data = ThisWorkbook.Worksheets("Sheet1").UsedRange.Value2
dataRows = UBound(data, 1)
dataCols = UBound(data, 2)
'Create a list of unique keys.
'Note: not the most efficient way, but shows how to
'create an array from a value-separated string.
For r = 1 To dataRows
If InStr(keys, CStr(data(r, 2))) = 0 Then
If Len(keys) > 0 Then keys = keys & "|"
keys = keys & CStr(data(r, 2))
End If
Next
keyArray = Split(keys, "|")
keyLen = UBound(keyArray)
'Initialise the row and column numbers for each key.
'Shows how to iterate an array using For Each loop.
ReDim rowNum(keyLen)
ReDim colNum(keyLen)
r = 1
i = 0
For Each k In keyArray
rowNum(i) = r
colNum(i) = 1
r = r + 3
i = i + 1
Next
'Count the number of items for each key.
'Shows how to iterate an array using For [index] loop.
ReDim keyCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
keyCount(i) = keyCount(i) + 1
If keyCount(i) > maxCount Then maxCount = keyCount(i)
Next
'Size the output array.
c = WorksheetFunction.Ceiling(maxCount / 3, 1)
ReDim output(1 To (keyLen + 1) * 3, 1 To c * dataCols + c - 1)
'Populate the output array.
ReDim threeCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
'Copy the columns for this row.
For c = 1 To dataCols
output(rowNum(i), colNum(i) + c - 1) = data(r, c)
Next
'Increment the count and if it's equals 3 then
'reset the row num and increase the column number.
threeCount(i) = threeCount(i) + 1
rowNum(i) = rowNum(i) + 1
If threeCount(i) = 3 Then
rowNum(i) = rowNum(i) - 3
colNum(i) = colNum(i) + dataCols + 1
threeCount(i) = 0
End If
Next
'Write the data - change "Sheet2" to your sheet name.
'Shows how to write an array to a Range.
ThisWorkbook.Worksheets("Sheet2").Range("A3") _
.Resize(UBound(output, 1), UBound(output, 2)).Value = output
End Sub
Private Function IndexOfKey(list() As String, key As String) As Long
Dim i As Long
Dim k As Variant
'Helper function to find index position of key in array.
For Each k In list
If key = k Then
IndexOfKey = i
Exit Function
End If
i = i + 1
Next
IndexOfKey = -1
End Function
I have an interesting problem. I need to fill in a 2D array with data, but I don't know how many data points there are until after the array is populated.
Dim finalArray(0 to 500000, 0 to 3)
R=0
For Each index in someDictionary
If Not someDictionary.item(index)(1) = 0
finalArray(R,0) = someDictionary.item(index)(1)
finalArray(R,1) = someDictionary.item(index)(2)
finalArray(R,2) = someDictionary.item(index)(3)
R = R + 1
End If
Next index
The issue is that I don't know how many items will be in the dictionary, nor how many will be nonzero. The only way I know is after I run the loop and have a count R.
Currently I'm printing the entire 500k row array to Excel, which is usually 100-400k rows of data with the rest blank. This is ungainly, and I would like to re-dimension the array to be the correct size. I can't use ReDim because I can't delete the data, and I can't use ReDim Preserve because it's 2-dimensional and I need to reduce the rows, not columns.
How about this?
Sub Sample()
Dim finalArray()
Dim R As Long
For Each Index In someDictionary
If Not someDictionary.Item(Index)(1) = 0 Then R = R + 1
Next Index
ReDim finalArray(0 To R, 0 To 3)
R = 0
For Each Index In someDictionary
If Not someDictionary.Item(Index)(1) = 0 Then
finalArray(R, 0) = someDictionary.Item(Index)(1)
finalArray(R, 1) = someDictionary.Item(Index)(2)
finalArray(R, 2) = someDictionary.Item(Index)(3)
R = R + 1
End If
Next Index
End Sub
You have a few options
Redim the array as needed rather then create up front (which I think is better than looping through the array twice :)). I have added this given your comment to tigeravatar
Transpose the array to redim the first dimension
Ignore the redundant data as per tigeravatar
Option 1
note I have flipped the data to make the point around testing for ReDim as you go rather than define an array size up front - it could well argue this changes the nature of the question but I thought the technique worth pointing out. Option2 shows how to transpose the array regardless
Sub SloaneDog()
Dim finalArray()
Dim R As Long
Dim lngCnt As Long
Dim lngCnt2 As Long
lngCnt = 100
ReDim finalArray(1 To 3, 1 To lngCnt)
somedictionary = Range("A1:C3001")
For lngCnt2 = 1 To UBound(somedictionary, 1)
finalArray(1, lngCnt2) = "data " & lngCnt2
If lngCnt2 Mod lngCnt = 0 Then ReDim Preserve finalArray(1 To 3, 1 To lngCnt2 + lngCnt)
Next
End Sub
Option 2
Sub EddieBetts()
Dim X()
Dim Y()
Dim LngCnt As Long
ReDim X(1 To 1000, 1 To 3)
Debug.Print UBound(X, 1)
LngCnt = 100
Y = Application.Transpose(X)
ReDim Preserve Y(1 To UBound(Y, 1), 1 To LngCnt)
X = Application.Transpose(Y)
Debug.Print UBound(X, 1)
End Sub
I'm having troubles getting my Error array to print to a range. I'm fairly sure I'm resizing it incorrectly, but I'm not sure how to fix it. I created a test add which just added garbage data from columns A and B, but normally AddPartError would be call from within various Subs/Functions, and then at the end of the main script process the array should be dumped onto a sheet. Here are the relevant functions:
Sub testadd()
For Each i In ActiveSheet.Range("A1:A10")
Call AddPartError(i.value, i.Offset(0, 1))
Next i
tmp = PartErrors
PrintArray PartErrors, ActiveWorkbook.Worksheets("Sheet1").[D1]
Erase PartErrors
tmp1 = PartErrors
PartErrorsDefined = 0
End Sub
Sub PrintArray(Data As Variant, Cl As Range)
Cl.Resize(UBound(Data, 1), 2) = Data
End Sub
Private Sub AddPartError(part As String, errType As String)
If Not PartErrorsDefined = 1 Then
ReDim PartErrors(1 To 1) As Variant
PartErrorsDefined = 1
End If
PartErrors(UBound(PartErrors)) = Array(part, errType)
ReDim Preserve PartErrors(1 To UBound(PartErrors) + 1) As Variant
End Sub
Ok. I did a bit of checking and the reason this doesn't work is because of your array structure of PartErrors
PartErrors is a 1 dimensional array and you are adding arrays to it, so instead of multi dimentional array you end up with a jagged array, (or array of arrays) when you actually want a 2d array
So to fix this, I think you need to look at changing your array to 2d. Something like the below
Private Sub AddPartError(part As String, errType As String)
If Not PartErrorsDefined = 1 Then
ReDim PartErrors(1 To 2, 1 To 1) As Variant
PartErrorsDefined = 1
End If
PartErrors(1, UBound(PartErrors, 2)) = part 'Array(part, errType)
PartErrors(2, UBound(PartErrors, 2)) = errType
ReDim Preserve PartErrors(1 To 2, 1 To UBound(PartErrors, 2) + 1) As Variant
End Sub
and
Sub PrintArray(Data As Variant, Cl As Range)
Cl.Resize(UBound(Data, 2), 2) = Application.Transpose(Data)
End Sub
NB. You also need to Transpose your array to fit in the range you specified.
You code is a little hard to follow, but redim clears the data that is in the array, so I think you need to use the "Preserve" keyword.
Below is some example code you can work through to give you the idea of how it works, but you will need to spend some time working out how to fit this into your code.
Good luck!
Sub asda()
'declare an array
Dim MyArray() As String
'First time we size the array I do not need the "Preserve keyword
'there is not data in the array to start with!!!
'Here we size it too 2 by 5
ReDim MyArray(1, 4)
'Fill Array with Stuff
For i = 0 To 4
MyArray(0, i) = "Item at 0," & i
MyArray(1, i) = "Item at 1," & i
Next
' "Print" data to worksheet
Dim Destination1 As Range
Set Destination1 = Range("a1")
Destination1.Resize(UBound(MyArray, 1) + 1, UBound(MyArray, 2) + 1).Value = MyArray
'Now lets resize that arrray
'YOU CAN ONLY RESIZE THE LAST SIZE OF THE ARRAY - in this case 4 to 6...
ReDim Preserve MyArray(1, 6)
For i = 5 To 6
MyArray(0, i) = "New Item at 0," & i
MyArray(1, i) = "New Item at 1," & i
Next
'and let put that next to our first list
' "Print" data to worksheet
Dim Destination2 As Range
Set Destination2 = Range("A4")
Destination2.Resize(UBound(MyArray, 1) + 1, UBound(MyArray, 2) + 1).Value = MyArray
End Sub
I am creating a search function that allows users to search up to 3 different properties at the same time in a database (prop1,2 and 3) and I have created this sub in VBA by putting the results for a searched prop into an array. However, now that I have up to 3 arrays I need to consolidate the arrays so that only the data that is duplicated in the arrays are displayed in the results. Is there any advice on how to 1) only look at the arrays for the properties that the user is searching for and 2) take only the data that is repeated into a final array so I can display it in a results range? Any help is greatly appreciated! Thanks!
Assuming that your entries are directly from a database and therefore are unique for one property, I can think of following steps for a simple solution:
Merge Arrays together (prop1, prop2, prop3 > temp)
Count occurrences for each element (in this example code tempCount)
Based on the knowledge about the occurrences, create the final array (here called result)
Dim prop1() As Variant
Dim prop2() As Variant
Dim prop3() As Variant
Dim temp() As Variant
Dim tempCount() As Integer
Dim result() As Variant
ReDim temp(UBound(prop1) + UBound(prop2) + UBound(prop3) + 1)
'merge arrays
Dim i As Integer
On Error Resume Next
For i = 0 To UBound(temp)
temp(i * 3) = prop1(i)
temp(i * 3 + 1) = prop2(i)
temp(i * 3 + 2) = prop3(i)
Next i
'count occurences
ReDim tempCount(UBound(temp) + 1)
Dim j As Integer
For i = 0 To UBound(temp)
tempCount(i) = 1
For j = 0 To i - 1
'comparison of elements
If temp(i) = temp(j) Then
tempCount(i) = tempCount(i) + 1
End If
Next j
Next i
ReDim result(UBound(temp) + 1)
'if an element occurs 3 times, add it to result
Dim count As Integer
count = 0
For i = 0 To UBound(tempCount)
If tempCount(i) = 3 Then
result(count) = temp(i)
count = count + 1
End If
Next i
To check for some samples I added this to the code. It simply prints out the arrays temp, result and tempCount to the columns A, B and C.
'some sample arrays
prop1 = Array("a", "b", "c", "d", "e")
prop2 = Array("b", "c", "f")
prop3 = Array("b", "c", "d", "g")
'some sample Output
'temp
Cells(1, 1).Value = "temp:"
For i = 0 To UBound(temp)
Cells(i + 2, 1).Value = temp(i)
Next i
'result
Cells(1, 2).Value = "result:"
For i = 0 To UBound(result)
Cells(i + 2, 2).Value = result(i)
Next i
'count:
Cells(1, 3).Value = "count:"
For i = 0 To UBound(tempCount)
Cells(i + 2, 3).Value = tempCount(i)
Next i
Notes: tempCount just holds the cumulative number of occurrences at the point the element is watched at.