Adding a dictionary to array - vba

I'd like to go through list in Excel and assign the values to dictionary. The the dictionary will be put into list. This will happen until the cells in the column are blank. As a result of entire function, the array of dictionaries will be returned.
Function CreateArrayofDicts()
Dim catlist As Variant
Dim catparams() As Variant
Dim ChartParamsDict As Dictionary
Dim k As Long
catlist = ArrayProblemsCat()
i = 1
Do Until IsEmpty(Cells(i, 1))
Set ChartParamsDict = New Scripting.Dictionary
For j = 0 To UBound(catlist)
Debug.Print Cells(i, 1)
If Cells(i, 1) = catlist(j) Then
Debug.Print 5
ChartParamsDict.Add Key:="Cells(1,2)", Item:=Cells(i, 2)
ChartParamsDict.Add Key:="Cells(1,3)", Item:=Cells(i, 3)
ChartParamsDict.Add Key:="Cells(1,4)", Item:=Cells(i, 4)
ReDim Preserve catparams(k)
catparams(k) = ChartParamsDict ' issues is here
k = k + 1
Debug.Print ChartParamsDict
End If
Next j
i = i + 1
Loop
CreateArrayofDicts = catparams
End Function

Your are missing Set on the problem line.
Set catparams(k) = ChartParamsDict

Related

VBA Permutate 1D array adding set value to each array item n number of times

I am trying to figure out a loop logic to get all possible permutations where I add a set value to each item in a set array iLoop number of times. I'm gonna try my best to explain what I am looking for.
I have a set value "StrokeValue" and a set array "DistanceMatesArray"
Dim StrokeValue as single
Dim DistanceMatesArray as variant
StrokeValue = 300
DistanceMatesArray = Array(300, 300, 300, 300)
Now I need to loop through each possible result where I add StrokeValue to each Item which in the first loop would result in possible DistanceMatesArrays:
The tricky part is when I want to add StrokeValue more than once and get every outcome where I added StrokeValue iLoop number of time "AllowedActions" resulting in a list such as:
I kind of suspect that I need a 2D array to store all the results from previous loop., that's why in the example the rows are coloured to indicate which one row was taken as a starting point to add the StrokeValue once
What I got so far looks like this:
Public StrokeValue As Single
Public DistanceMatesArray As Variant
Public iError As Long
Public NumberOfCombinations As Long
Public x As Long
Public y As Long
Public i As Long
Option Explicit
Sub Test()
'Declare variables
Dim PreviousLoopResultsArray As Variant
Dim NextLoopResultsArray As Variant
Dim iresults As Long
Dim iLoop As Long
Dim iPreviousResult As Long
'Set variables
StrokeValue = 300
'Array
DistanceMatesArray = Array(300, 300, 300, 300)
ReDim NextLoopResultsArray(0, UBound(DistanceMatesArray))
For i = LBound(DistanceMatesArray) To UBound(DistanceMatesArray)
NextLoopResultsArray(0, i) = DistanceMatesArray(i)
Next i
'------------------------------------------------------
'Loop
Do While iError = NumberOfCombinations
'Try DistanceMatesArray
For i = 0 To iresults
For x = 0 To UBound(DistanceMatesArray) 'Loop horizontally
DistanceMatesArray(x) = NextLoopResultsArray(i, x)
Next x
Debug.Print Join(DistanceMatesArray)
'TRY HERE
Next i
'Array
PreviousLoopResultsArray = NextLoopResultsArray
'Array
If iLoop <> 0 Then
For x = 0 To UBound(DistanceMatesArray) 'Loop horizontally
DistanceMatesArray(x) = PreviousLoopResultsArray(iPreviousResult, x)
Next x
End If
'Set variables
iLoop = iLoop + 1
iPreviousResult = 1
iresults = ((UBound(DistanceMatesArray) + 1) ^ iLoop) - 1
ReDim NextLoopResultsArray(iresults, UBound(DistanceMatesArray))
'Populate NextLoopResultsArray
For y = 0 To iresults 'Loop vertically
If y Mod (UBound(DistanceMatesArray) + 1) = 0 And y <> iresults And y <> 0 Then
For x = 0 To UBound(DistanceMatesArray) 'Loop horizontally
DistanceMatesArray(x) = PreviousLoopResultsArray(iPreviousResult, x)
Next x
iPreviousResult = iPreviousResult + 1
End If
For x = 0 To UBound(DistanceMatesArray) 'Loop horizontally
NextLoopResultsArray(y, x) = DistanceMatesArray(x)
With ThisWorkbook.Worksheets(1).Cells(y + 1, x + 1)
.Value = NextLoopResultsArray(y, x)
End With
Next x
Next y
'Modify NextLoopResultsArray
x = 0
For y = 0 To iresults 'Loop vertically
NextLoopResultsArray(y, x) = NextLoopResultsArray(y, x) + StrokeValue
With ThisWorkbook.Worksheets(1).Cells(y + 1, x + 1)
.Value = NextLoopResultsArray(y, x)
.Interior.Color = vbYellow
End With
If x + 1 > UBound(DistanceMatesArray) Then
x = 0
Else
x = x + 1
End If
Next y
'Set variables
iPreviousResult = 0
'Excel reset
For i = 1 To (UBound(DistanceMatesArray) + 1)
Columns(i).Clear
Next i
Loop
End Sub
At the end of the loop I am expecting to have each one row as DistanceMatesArray i.e. one of them would now be
DistanceMatesArray = array(300,600,600,300)
Where it added StrokeValue twice.
Would someone, please, help me figure out a shorter and simpler logic behind this?
EDIT:
Results expected after running it up to 3 loops looks like this:
And without duplicate outcomes
Continuing to try and figure out the logic of it, maybe now someone get's a betetr idea for what I am lookign for and can help
No need to mention that it's an infinite loop - I know that and That's the point, it needs to go on untill I validate the right array in which case iError <> NumberOfCombinations.
Been able to learn more about arrays, so I consider this a big win.
The code took in account the duplicates but for now your iterations are hardset (could easily ask how many iterations with an inputbox), not in the endless loop you had set up, hope that rework won't be too much.
Some variables are reworked, I tried to keep most of your original ones though.
Public StrokeValue As Single
Public DistanceMatesArray As Variant
Public iError As Long
Public iTerations As Long
Public i As Long
Public j As Long
Public k As Long
Option Explicit
Sub TestArrayfill()
Dim pArray As Variant, nArray As Variant, cArray As Variant 'pArray = previous array, nArray = next array, cArray = check array
Dim iresults As Long, iLoop As Long, nb As Long, actB As Long, addCounter As Long, Lastrow As Long
'Set variables
StrokeValue = 300
addCounter = 1
iTerations = 4
'Array
DistanceMatesArray = Array(300, 300, 300, 300)
nb = UBound(DistanceMatesArray) + 1
ReDim Preserve DistanceMatesArray(1 To nb)
cArray = DistanceMatesArray
ReDim pArray(1 To nb, 1 To nb)
For i = 1 To nb
pArray(1, i) = DistanceMatesArray(i)
Next i
actB = nb
For iLoop = 1 To iTerations 'I can't figure out the limitations with permutations so we'll just bruteforce it with nb*actB (maximum possibilities)
ReDim nArray(1 To nb * actB, 1 To nb) '(re)setting nArray
If iLoop = 1 Then actB = 1 'workaround to have pArray as a 2D-array
For i = 1 To actB 'loop through every row in pArray except for when iLoop = 1
For j = 1 To nb 'loop through every cell in pArray(i)
For k = 1 To nb 'setting the extra StrokeValue
If j = k Then
cArray(k) = pArray(i, k) + StrokeValue
Else
cArray(k) = pArray(i, k)
End If
Next k
If Not arrElemInArray(cArray, nArray) Then
For k = 1 To nb
nArray(addCounter, k) = cArray(k) 'add the "row" to our nArray
Next k
addCounter = addCounter + 1
End If
Next j
Next i
actB = addCounter - 1
ReDim pArray(1 To actB, 1 To nb) 'ReDim is possible on both dimensions, Redim Preserve is not so we use this to our advantage
For i = 1 To actB 'another loop is necessary however
For j = 1 To nb
pArray(i, j) = nArray(i, j)
Next j
Next i
' nArray = Application.Transpose(nArray)
' ReDim Preserve nArray(1 To nb, 1 To actB)
' nArray = Application.Transpose(nArray)
' pArray = Application.Transpose(pArray)
' ReDim pArray(1 To UBound(nArray, 2), UBound(nArray, 1))
' pArray = Application.Transpose(pArray)
' pArray = nArray
addCounter = 1
Lastrow = Range("A" & Rows.Count).End(xlUp).Row
If Lastrow = 1 Then
Cells(Lastrow, 1).Value = "Loop" & iLoop
Else
Cells(Lastrow + 1, 1).Value = "Loop " & iLoop
Lastrow = Lastrow + 1
End If
Cells(Lastrow + 1, 1).Resize(UBound(nArray, 1), UBound(nArray, 2)) = nArray
Next iLoop
End Sub
Function arrElemInArray(arr As Variant, arrX As Variant) As Boolean 'this is from one of your previous questions btw, just a bit modified to fit our needs
Dim i As Long, j As Long, boolFound As Boolean, mtch
If Not IsArray(arrX) Then
For j = LBound(arr) To UBound(arr)
If arr(j) = arrX Then arrElemInArray = True: Exit For
Next j
Exit Function
End If
For i = LBound(arrX) To UBound(arrX)
boolFound = True 'True at beginning so if any cells deviates from the corresponding check, it gets set to False, ergo it doesn't exist yet.
For j = LBound(arr) To UBound(arr)
If arr(j) <> arrX(i, j) Then
boolFound = False
End If
Next j
If boolFound Then arrElemInArray = True: Exit Function
Next i
arrElemInArray = False
End Function
Hope it's all clear and works for you :)
I understood your logic for the first table
but for the following ones I find it difficult to understand what you want especially in relation to the capture that you put in your message
for the first
Sub testing()
Dim StrokeValue As Single
Dim DistanceMatesArray As Variant
Dim i As Long 'variable row iteration
Dim c As Long 'variable column itération
Dim Table As Variant 'variable variant no dimention in the first
StrokeValue = 300
DistanceMatesArray = Array(300, 300, 300, 300) 'is an array in base 0
nb = UBound(DistanceMatesArray) + 1 'convert a ubound of DistanceMatesArray in count (in base 1)
ReDim Table(1 To nb, 1 To nb) 'table dimensioning (variant) in base 1
'loop for row
For i = 1 To UBound(Table) 'start at index 1
'loop for column
For c = 1 To UBound(Table, 2) 'start at index 1
'if index row and index column then item has multipled by (2)
If c <> i Then Table(i, c) = StrokeValue Else Table(i, c) = StrokeValue + StrokeValue
Next c
Next i
'just for see on sheet
Cells.Resize(UBound(Table), UBound(Table)) = Table
End Sub

excel vba I need to transpose data from columns to rows

I am looking for a VBA solution to transform data from a scenario similar to the illustration below. From Sheet1 copy first three cell values (A3,B3,C3) only if there is a value in any cell to the left of them (D3,E3,...) in Sheet2 past first 3 cell values (A2,B2,C2), and the first cell after that with a value (D3) and also copy the header value into the adjacent cell. Any additional values to the left get the same treatment and become the next row, again copying (A3,B3,C3). Then the next adjacent cell value (E3) along with the header value into the adjacent cell. Then move down to the next row in Sheet1 where there are values after the first 3 cells until it has looped all the way through sheet1 to produce the example in Sheet2.
I have searched for other similar solutions but cannot find anything that works. This is the closest I've found with minor edits on my part but doesn’t work, any help is greatly appreciated.
Sub Sample()
Dim wsThis As Worksheet
Dim wsThat As Worksheet
Dim ThisAr As Variant
Dim ThatAr As Variant
Dim Lrow As Long
Dim Col As Long
Dim i As Long
Dim k As Long
Set wsThis = Sheet1: Set wsThat = Sheet2
With wsThis
'~~> Find Last Row in Col A
Lrow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Find total value in D,E,F so that we can define output array
Col = Application.WorksheetFunction.CountA(.Range("C2:G" & Lrow))
'~~> Store the values from the range in an array
ThisAr = .Range("A2:G" & Lrow).Value
'~~> Define your new array
ReDim ThatAr(1 To Col, 1 To 7)
'~~> Loop through the array and store values in new array
For i = LBound(ThisAr) To UBound(ThisAr)
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
'~~> Check for Color 1
If ThisAr(i, 5) <> "" Then 'ThatAr(k, 4) = ThisAr(i, 4)
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
ThatAr(k, 4) = ThisAr(i, 4)
ThatAr(k, 5) = ThisAr(i, 5)
End If
'~~> Check for Color 2
If ThisAr(i, 7) <> "" Then
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
ThatAr(k, 6) = ThisAr(i, 6)
ThatAr(k, 7) = ThisAr(i, 7)
End If
'~~> Check for Color 3
'If ThisAr(i, 6) <> "" Then
'k = k + 1
'ThatAr(k, 1) = ThisAr(i, 1)
'ThatAr(k, 2) = ThisAr(i, 2)
'ThatAr(k, 3) = ThisAr(i, 3)
'ThatAr(k, 4) = ThisAr(i, 6)
'End If
Next i
End With
'~~> Create headers in Sheet2
Sheet2.Range("A1:D1").Value = Sheet1.Range("A1:D1").Value
'~~> Output the array
wsThat.Range("A2").Resize(Col, 4).Value = ThatAr
End Sub
Using a variant array(dynamic array) is simple and fast.
Sub test()
Dim wsThis As Worksheet, wsThat As Worksheet
Dim vDB As Variant, vR() As Variant
Dim r As Long, i As Long, n As Long
Dim c As Integer, j As Integer, k As Integer
Set wsThis = Sheet1: Set wsThat = Sheet2
vDB = wsThis.Range("a1").CurrentRegion
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 2 To r
For j = 4 To c
If vDB(i, j) <> "" Then
n = n + 1
ReDim Preserve vR(1 To 5, 1 To n)
For k = 1 To 3
vR(k, n) = vDB(i, k)
Next k
vR(4, n) = vDB(i, j)
vR(5, n) = vDB(1, j)
End If
Next j
Next i
With wsThat
.UsedRange.Clear
.Range("a1").Resize(1, 3) = wsThis.Range("a1").Resize(1, 3).Value
.Range("d1").Resize(1, 2) = Array("Value", "ID#")
.Range("a2").Resize(n, 5) = WorksheetFunction.Transpose(vR)
End With
End Sub
Sorry, I'm not sure why I couldn't open your attached images.
But you might want to try this code:
Change this line:
wsThat.Range("A2").Resize(Col, 4).Value = ThatAr
To
wsThat.Range("A2").Resize(4, Col).Value = WorksheetFunction.Transpose(ThatAr)
Hope this help

Excel VBA - Auto FIlter and Advanced filter usage error

I have a requirement where in, I need to use the auto filter to filter the data first and then am using the advanced filter to get the Unique values alone. But the advanced filter doesn't take the auto filtered value alone. How do I use them together?
Here goes my code,
Colmz = WorksheetFunction.Match("RSDate", Sheets("RS_Report").Rows(1), 0)
ActiveSheet.ListObjects("RS").Range.AutoFilter Field:=Colmz, Criteria1:="YES"
ActiveSheet.Range("B1:B65536").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Sheets("CSRS").Range("B14"), Unique:=True
Kindly correct me and share your suggestions. Thanks
I would stick the unique values in an array - it's faster and less likely to break -
sub uniquearray()
Colmz = WorksheetFunction.Match("RSDate", Sheets("RS_Report").Rows(1), 0)
ActiveSheet.ListObjects("RS").Range.AutoFilter Field:=Colmz, Criteria1:="YES"
Call creatary(curary, Sheets("RS_Report"), Letter(Sheets("RS_Report"), "RSDate")): Call eliminateDuplicate(curary): Call BuildArrayWithoutBlankstwo(curary): Call Alphabetically_SortArray(curary)
For Each cell In curary
'do what you need to do with the unique array list
Next cell
end sub
Function creatary(ary As Variant, sh As Worksheet, ltr As String)
Dim x, y, rng As Range
ReDim ary(0)
Set rng = sh.Range(ltr & "2:" & ltr & sh.Range("A1000000").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
x = 0
For Each y In rng
If Not Application.IsError(y) Then
If Not IsNumeric(y) Then
ary(x) = y
End If
x = x + 1
ReDim Preserve ary(x)
End If
Next y
End Function
Function BuildArrayWithoutBlankstwo(ary As Variant)
Dim AryFromRange() As Variant, AryNoBlanks() As Variant
Dim Counter As Long, NoBlankSize As Long
'set references and initialize up-front
ReDim AryNoBlanks(0 To 0)
NoBlankSize = 0
'load the range into array
AryFromRange = ary
'loop through the array from the range, adding
'to the no-blank array as we go
For Counter = LBound(AryFromRange) To UBound(AryFromRange)
If ary(Counter) <> 0 Then
NoBlankSize = NoBlankSize + 1
AryNoBlanks(UBound(AryNoBlanks)) = ary(Counter)
ReDim Preserve AryNoBlanks(0 To UBound(AryNoBlanks) + 1)
End If
Next Counter
'remove that pesky empty array field at the end
If UBound(AryNoBlanks) > 0 Then
ReDim Preserve AryNoBlanks(0 To UBound(AryNoBlanks) - 1)
End If
'debug for reference
ary = AryNoBlanks
End Function
Function eliminateDuplicate(ary As Variant) As Variant
Dim aryNoDup(), dupArrIndex, i, dupBool, j
dupArrIndex = -1
For i = LBound(ary) To UBound(ary)
dupBool = False
For j = LBound(ary) To i
If ary(i) = ary(j) And Not i = j Then
dupBool = True
End If
Next j
If dupBool = False Then
dupArrIndex = dupArrIndex + 1
ReDim Preserve aryNoDup(dupArrIndex)
aryNoDup(dupArrIndex) = ary(i)
End If
Next i
ary = aryNoDup
End Function
Function Alphabetically_SortArray(ary)
Dim myArray As Variant
Dim x As Long, y As Long
Dim TempTxt1 As String
Dim TempTxt2 As String
myArray = ary
'Alphabetize Sheet Names in Array List
For x = LBound(myArray) To UBound(myArray)
For y = x To UBound(myArray)
If UCase(myArray(y)) < UCase(myArray(x)) Then
TempTxt1 = myArray(x)
TempTxt2 = myArray(y)
myArray(x) = TempTxt2
myArray(y) = TempTxt1
End If
Next y
Next x
ary = myArray
End Function
Function Letter(oSheet As Worksheet, name As String, Optional num As Integer)
If num = 0 Then num = 1
Letter = Application.Match(name, oSheet.Rows(num), 0)
Letter = Split(Cells(, Letter).Address, "$")(1)
End Function

VBA Excel Finding and Combining Rows Based on Matching Column Cells

I'm trying to figure out a way to combine rows based on values in two specific columns in vba excel.
For Example:
Let's say I have the following sheet:
Column A Column J Column Z
1 A ?
1 A !
2 B ?
2 B !
And I need to convert it to this:
Column A Column J Column Z
1 A ?, !
2 B ?, !
Here's another method using User Defined Types and collections to iterate through the list and develop the combined results. For large sets of data, it should be considerably faster than reading through each cell on the worksheet.
I assume that you are grouping on Col J, and that Column A data does not need to be concatenated in the cell. If it does, the modifications to the routine would be trivial.
First, Insert a Class Module, rename it CombData and insert the following code into that module:
Option Explicit
Private pColA As String
Private pColJ As String
Private pColZConcat As String
Public Property Get ColA() As String
ColA = pColA
End Property
Public Property Let ColA(Value As String)
pColA = Value
End Property
Public Property Get ColJ() As String
ColJ = pColJ
End Property
Public Property Let ColJ(Value As String)
pColJ = Value
End Property
Public Property Get ColZConcat() As String
ColZConcat = pColZConcat
End Property
Public Property Let ColZConcat(Value As String)
pColZConcat = Value
End Property
Then Insert a Regular Module and insert the Code Below:
Option Explicit
Sub CombineData()
Dim cCombData As CombData
Dim colCombData As Collection
Dim V As Variant
Dim vRes() As Variant 'Results Array
Dim rRes As Range 'Location of results
Dim I As Long
'read source data into array
V = Range("A1", Cells(Rows.Count, "A").End(xlUp)).Resize(columnsize:=26)
'Set results range. Here it is set below the Source Data
'Could be anyplace, even on a different worksheet; or could overlay the
' original. Area below and to right is cleared
Set rRes = Range("A1").Offset(UBound(V) + 10)
Range(rRes, rRes.SpecialCells(xlCellTypeLastCell)).Clear
Set colCombData = New Collection
On Error Resume Next
For I = 1 To UBound(V)
Set cCombData = New CombData
cCombData.ColA = V(I, 1)
cCombData.ColJ = V(I, 10)
cCombData.ColZConcat = V(I, 26)
colCombData.Add cCombData, CStr(cCombData.ColJ)
If Err.Number <> 0 Then
Err.Clear
With colCombData(cCombData.ColJ)
.ColZConcat = .ColZConcat & ", " & V(I, 26)
End With
End If
Next I
On Error GoTo 0
ReDim vRes(1 To colCombData.Count, 1 To 26)
For I = 1 To UBound(vRes)
With colCombData(I)
vRes(I, 1) = .ColA
vRes(I, 10) = .ColJ
vRes(I, 26) = .ColZConcat
End With
Next I
rRes.Resize(UBound(vRes, 1), UBound(vRes, 2)) = vRes
End Sub
EDIT: Note that the source data is read into the Variant array V. If you examine V in the Watch Window, you will see that the first dimension represents the rows; and the second dimension the columns. So if you wanted, for example, to perform the same procedure on a different set of columns, you would merely change the references to the second dimension under the line that reads Set cCombData = New CombData. For example, column B data would be V(I,2), and so forth. Of course, you might want to rename the data types to make them more representative of what you are doing.
In addition, if your data starts at row 2, merely start the iteration through V with I = 2 instead of I = 1.
EDIT2: In order to both overwrite the original, and also maintain the contents of the columns not being processed, the following modification will do that for Columns A, J and Z. You should be able to modify it for whatever columns you choose to process.
Option Explicit
Sub CombineData()
Dim cCombData As CombData
Dim colCombData As Collection
Dim V As Variant
Dim vRes() As Variant 'Results Array
Dim rRes As Range 'Location of results
Dim I As Long, J As Long, K As Long
'read source data into array
V = Range("A1", Cells(Rows.Count, "A").End(xlUp)).Resize(columnsize:=26)
'Set results range. Here it is set below the Source Data
'Could be anyplace, even on a different worksheet; or could overlay the
' original. Area below and to right is cleared
Set rRes = Range("A1") '.Offset(UBound(V) + 10)
Range(rRes, rRes.SpecialCells(xlCellTypeLastCell)).Clear
Set colCombData = New Collection
On Error Resume Next
For I = 1 To UBound(V)
Set cCombData = New CombData
cCombData.ColA = V(I, 1)
cCombData.ColJ = V(I, 10)
cCombData.ColZConcat = V(I, 26)
colCombData.Add cCombData, CStr(cCombData.ColJ)
If Err.Number <> 0 Then
Err.Clear
With colCombData(cCombData.ColJ)
.ColZConcat = .ColZConcat & ", " & V(I, 26)
End With
End If
Next I
On Error GoTo 0
ReDim vRes(1 To colCombData.Count, 1 To 26)
For I = 1 To UBound(vRes)
With colCombData(I)
vRes(I, 1) = .ColA
vRes(I, 10) = .ColJ
vRes(I, 26) = .ColZConcat
'Note the 10 below is the column we are summarizing by
J = WorksheetFunction.Match(.ColJ, WorksheetFunction.Index(V, 0, 10), 0)
For K = 1 To 26
Select Case K 'Decide which columns to copy over
Case 2 To 9, 11 To 25
vRes(I, K) = V(J, K)
End Select
Next K
End With
Next I
rRes.Resize(UBound(vRes, 1), UBound(vRes, 2)) = vRes
End Sub
This is assuming that Column J is the key and Column A doesn't need to be appended. If Column A needs to be combined as well (not always the same), you would simply need to add another for each loop to check if the data is there, and add it if not, as done for col 26 in the code.
Sub CombineData()
x = 2
Do Until Cells(x, 1) = "" 'loop through every row in sheet starting at 2 (1 will never be removed, since it is the first data)
x2 = 1
Do Until x2 = x
If Cells(x, 10) = Cells(x2, 10) Then 'this is comparing column J. If another column is the reference then change 10 to the column number
splt = Split(Cells(x, 26), ", ")
For Each s In splt 'check to see if data already in column z
If s = Cells(x2, 26) Then GoTo alreadyEntered
Next
Cells(x, 26) = Cells(x, 26) & ", " & Cells(x2, 26) 'append column z data to row x
alreadyEntered:
Rows(x2).Delete Shift:=xlUp 'delete duplicate row
x = x - 1 'to keep x at same row, since we just removed a row
Exit Do
Else
x2 = x2 + 1
End If
Loop
x = x + 1
Loop
End Sub

ReDiming an array in VBA

I have a serious problem with resizing a 2-dimensional array in VBA. I've done a lot of reading about this (popular) issue, but still I can't figure out what's wrong in my code.
So, I have some data in a spreadsheet. In the second row I have some descriptions of an element, while in the first row I have categories of those elements. What I want to do is create an array which has (distinct) categories in the first row and indexes of descriptions related to a particular category in the second row.
The code works correctly up until
If j = UBound(distinctList, 2) Then
Then ReDim comes in and I get a "Subscript out of range error".
That If is there to add a new category and is meant to kick in if the entry from the spreadsheet does not equal any entry from the new array.
Function distinctValues(arr)
Dim distinctList() As String
Dim j As Integer
k = 0
'ReDim distinctList(0 To 0, 0 To 1)
'Dodaj pierwszy wpis
For i = LBound(arr) To UBound(arr)
If arr(i) <> "" Then
ReDim distinctList(0 To 1, 0 To j)
distinctList(0, 0) = arr(i)
distinctList(1, 0) = i + 1
'k = k + 1
Exit For
End If
Next i
'Dodaj kolejne wpisy
For i = LBound(arr) + 1 To UBound(arr)
If arr(i) <> "" Then
For j = LBound(distinctList, 2) To UBound(distinctList, 2)
If arr(i) = distinctList(0, j) Then
distinctList(1, j) = distinctList(1, j) & ", " & i + 1
'k = k + 1
Exit For
End If
If j = UBound(distinctList, 2) Then
ReDim Preserve distinctList(0 To 1, 1 To UBound(distinctList, 2) + 1)
distinctList(0, j) = arr(i)
distinctList(1, j) = distinctList(UBound(distinctList, 2), 1) & ", " & i + 1
Exit For
End If
Next j
End If
Next i
Debug.Print distinctList(0, 0) & " => " & distinctList(1, 0)
'distinctValues = distinctList
End Function
It's because you can't change the lower bound of the second dimension, you need to keep it the same..
You declare ReDim distinctList(0 To 1, 0 To j) at the top
when you redim, you need to keep lower bound of the second dimension at 0
ReDim Preserve distinctList(0 To 1, 0 To UBound(distinctList, 2) + 1)
I think you could implement this general solution to your particular solution if you apply this code to change the nr. of dimensions before you add the/a new category.
Option Explicit
Public Sub redimarray()
'This sub redimensions an array as an array of arrays, so to acces the k'th element in the n-th dimension you need to type: my_array(n)(k)
'and you can still simply redefine the array dimensions by:
'my_array =FlexArray("lower_bound_n-th_dim,lower_bound_n-th_dim,_n+1-th_dim,upper_bound_n-th_dim,_n+1-th_dim) = e.g.: FlexArray("2,3,9,11")
'if you then want to have conventional array element conventional_array(3,4) you can copy the entire my_array into a 1 dimensional array where
' the array elements are added like a (nr-of_elements_per_dimension)-base numbering system. once they have been manipulated, you can store them back into
'nr of elements per dimension:
'dim 0 = 4, 0-3
'dim 1 = 3, 4-6
'dim 2 = 8, 1-8
'nr of elements in 1dim array = 4*3*8 = 96
'(0)(4)(1)
'(0)(4)(2)
'...
'(0)(4)(8)
'(0)(5)(1)
'so working_array(3,5,2) = (3-0)*nr_elem(dim 1)*nr_elem(dim 2)+(5-4)*nr_elem(dim 2)+(2-1)
'dim 0 = nr_elements(0), start_element(0)-end_element(0)
'dim 1 = nr_elements(1), start_element(1)-end_element(1)
'dim 2 = nr_elements(2), start_element(2)-end_element(2)
'so working_array(3,5,2) = (end_element(0)-start_element(0))*nr_elements(1)*nr_elements(2)+(end_element(1)-start_element(1))*nr_elements(2)+'so working_array(3,5,2) = (end_element(0)-start_element(0))*nr_elements(1)*nr_elements(2)+(end_element(2)-start_element(2))=index in 1 dimensional array.
Dim NewArray() As Variant
NewArray = FlexArray("1,2,3,8,2,9")
'NewArray = FlexibleArray("1,2,3,8,2,9")
MsgBox (NewArray(1)(8))
End Sub
Public Function FlexArray(strDimensions As String) As Variant
Dim arrTemp As Variant
Dim varTemp As Variant
Dim varDim As Variant
Dim intNumDim As Integer
Dim iDim As Integer
Dim iArr As Integer
varDim = Split(strDimensions, ",")
intNumDim = (UBound(varDim) + 1) / 2
' Setup redimensioned source array
ReDim arrTemp(intNumDim)
iArr = 0
For iDim = LBound(varDim) To UBound(varDim) Step 2
ReDim varTemp(varDim(iDim) To varDim(iDim + 1))
arrTemp(iArr) = varTemp
iArr = iArr + 1
Next iDim
FlexArray = arrTemp
End Function