dynamically populate and print 2D array in vba - arrays

I am looping through a 2D array in order to find if the values from column 1 are already present on the sheet. If they are, I would like to paste the value from column 2 in the same column it was found. The code I have is below... I am not receiving any errors but there are not any values being printed.
The range I am pasting into needs to be dynamic and right now is based on the array titled "myArrayNoBlanks" (a dynamic array that populates the original values I am pulling for my data). Perhaps changing the range to something that would find the last column would do the trick?
Any and all help is greatly appreciated.
For h = 1 To 11
Set PartRange = Worksheets(12).UsedRange.Columns(col(h))
Set CostRange = Worksheets(12).UsedRange.Columns(Cost(h))
For Each cell In PartRange.Cells
If cell.Row > 1 Then
ReDim Preserve myParts(var)
myParts(var) = cell.Value
var = var + 1
End If
Next cell
For Each cell In CostRange.Cells
If cell.Row > 1 Then
ReDim Preserve myCost(varc)
myCost(varc) = cell.Value
varc = varc + 1
End If
Next cell
myNewArray = Array(myParts, myCost)
For w = LBound(myNewArray, 1) To UBound(myNewArray, 1)
Dim cols As Integer
Dim ans As Boolean
Dim sch As Long
sch = myNewArray(w)(2)
Dim ch As Range
Dim rngs As Range
Set rngs = Range("B1")
Set rngs = rngs.Resize(1, UBound(myArrayNoBlanks))
For Each ch In rngs
If ch = sch Then
ans = True
cols = ch.Column
If h = 1 Then
Dim Dest1 As Range
Set Dest1 = Worksheets(14).Cells(2, cols)
Dest1.Value = sch
End If
End If
Next ch
If IsNull(ans) Then ans = False
Next w

Related

Loop Through Org Employee ID Arrays using Begin With Filter

I have a report with a summary page that has all of the employee ID's listed and each employee belongs to a specific group. I'm trying to have my macro filter through the arrays for each group with each array containing the EID numbers and then export the filtered data into a separate sheet.
The issue I'm running into is, I have one group that contains about 20 EIDs and I'm using the "begin with filtering method" such as "1156*" which only seems to work with up to two values in the array only. I'm using this method because the EID's in the summary page are shown for example "11569-Org1". Any help to work around this would be appreciated.
Dim EIDNumbers(1 to 3) As Variant
EIDNumbers(1) = Array("16799*", "17900*")
EIDNumbers(2) = "22222*"
EIDNumbers(3) = Array("88888*","90000*","88444*")
For n = UBound(GroupNames) To LBound(GroupNames) Step -1
If IsArray(EIDNumbers(n)) Then
dataRG.AutoFilter 11, EIDNumbers(n), xlFilterValues
Else
dataRG.AutoFilter 11, EIDNumbers(n)
End If
Set fdataRG = mainWS.Range("A1").EntireColumn
fdataCT = Application.WorksheetFunction.Subtotal(103, fdataRG) - 1
If fdataCT > 1 Then ' add additional subws
Set subWS = wb.Worksheets.Add(After:=mainWS)
subWS.Name = OrgNames(n)
Set dfcell = subWS.Range("A1")
dataRG.SpecialCells(xlCellTypeVisible).Copy dfcell
End If
Next n
Filter Data When More Than Two WildCard Criteria
Dim LeftEIDs(1 To 3) As Variant
LeftEIDs(1) = Array("16799*", "17900*")
LeftEIDs(2) = Array("22222*")
LeftEIDs(3) = Array("88888*", "90000*", "88444*")
' Write the data (the EIDs) from the EID column to the keys of a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim Data: Data = dataRG.Columns(11).Value
Dim rCount As Long: rCount = UBound(EIDs, 1)
Dim r As Long, cString As String
For r = 2 To rCount
cString = CStr(Data(r, 1))
If Len(cString) > 0 Then dict(cString) = Empty
Next r
' Write the matches per group to the keys of another dictionary
' and use them to filter by the EID column and to copy
' the data to a new worksheet.
Dim cDict As Object: Set cDict = CreateObject("Scripting.Dictionary")
cDict.CompareMode = vbTextCompare
Dim LeftEID, eKey, e As Long, n As Long
If mainWS.FilterMode Then mainWS.ShowAllData ' not sure about 'mainWS'!?
For n = UBound(LeftEIDs) To LBound(LeftEIDs) Step -1
For Each eKey In dict.Keys
For Each LeftEID In LeftEIDs(n)
If eKey Like LeftEID Then
cDict(eKey) = Empty
dict.Remove eKey
Exit For
End If
Next LeftEID
Next eKey
If cDict.Count > 0 Then
dataRG.AutoFilter 11, cDict.Keys, xlFilterValues
Set subWS = wb.Worksheets.Add(After:=mainWS)
subWS.Name = OrgNames(n)
Set dfcell = subWS.Range("A1")
dataRG.SpecialCells(xlCellTypeVisible).Copy dfcell
mainWS.ShowAllData ' not sure about 'mainWS'!?
cDict.RemoveAll
End If
Next n
'mainWS.autfiltermode = False ' not sure about 'mainWS'!?

Type mismatch when converting from a Collection to an Array

I have a code where I'm adding last rows to collection which should be later transformed to an array and final step is to get last row with the least number of cells in it.
My current code is:
Dim lastc, lastc2, lastr, FindColNumber, FindColNumber2 as Long
Dim FindCol as Range
Dim col As New Collection
Dim CollectionToArray As Variant
Set FindCol = 1 'example
FindColNumber = FindCol.Column
lastc = FindColNumber + 1
Set FindCol2 = 5 'example
FindColNumber = FindCol2.Column
lastc2 = FindColNumber - 1
For R = lastc2 To lastc Step -1
lastc2 = R
col.Add Cells(ws.Rows.count, R).End(xlUp).Row
Next R
Debug.Print WorksheetFunction.Min(CollectionToArray(col))
Public Function CollectionToArray(myCol As Collection) As Variant
Dim result As Variant
Dim cnt As Long
ReDim result(myCol.count - 1)
For cnt = 0 To myCol.count - 1
result(cnt) = myCol(cnt + 1)
Next cnt
CollectionToArray = result
End Function
I am still getting Type Mismatch error and when hovering over CollectionToArray(Col)) I see "Object variable or With block variable not set".
Thank you.

Excel VBA - Determining Column or Row Target of Array UDF

I have a simple excel UDF for converting an array of mass values to mol fractions. Most times, the output will be a column array (n rows by 1 column).
How, from within the VBA environment, do I determine the dimensions of the target cells on the worksheet to ensure that it should be returned as n rows by 1 column versus n columns by 1 row?
Function molPct(chemsAndMassPctsRng As Range)
Dim chemsRng As Range
Dim massPctsRng As Range
Dim molarMasses()
Dim molPcts()
Set chemsRng = chemsAndMassPctsRng.Columns(1)
Set massPctsRng = chemsAndMassPctsRng.Columns(2)
chems = oneDimArrayZeroBasedFromRange(chemsRng)
massPcts = oneDimArrayZeroBasedFromRange(massPctsRng)
'oneDimArrayZeroBasedFromRange is a UDF to return a zero-based array from a range.
ReDim molarMasses(UBound(chems))
ReDim molPcts(UBound(chems))
totMolarMass = 0
For chemNo = LBound(chems) To UBound(chems)
molarMasses(chemNo) = massPcts(chemNo) / mw(chems(chemNo))
totMolarMass = totMolarMass + molarMasses(chemNo)
Next chemNo
For chemNo = LBound(chems) To UBound(chems)
molPcts(chemNo) = Round(molarMasses(chemNo) / totMolarMass, 2)
Next chemNo
molPct = Application.WorksheetFunction.Transpose(molPcts)
End Function
I understand that, if nothing else, I could have an input parameter to flag if return should be as a row array. I'm hoping to not go that route.
Here is a small example of a UDF() that:
accepts a variable number of input ranges
extracts the unique values in those ranges
creates a suitable output array (column,row, or block)
dumps the unique values to the area
Public Function ExtractUniques(ParamArray Rng()) As Variant
Dim i As Long, r As Range, c As Collection, OutPut
Dim rr As Range, k As Long, j As Long
Set c = New Collection
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' First grab all the data and make a Collection of uniques
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
For i = LBound(Rng) To UBound(Rng)
Set r = Rng(i)
For Each rr In r
c.Add rr.Value, CStr(rr.Value)
Next rr
Next i
On Error GoTo 0
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' next create an output array the same size and shape
' as the worksheet output area
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
k = 1
With Application.Caller
ReDim OutPut(1 To .Rows.Count, 1 To .Columns.Count)
End With
For i = LBound(OutPut, 1) To UBound(OutPut, 1)
For j = LBound(OutPut, 2) To UBound(OutPut, 2)
If k < c.Count + 1 Then
OutPut(i, j) = c.Item(k)
k = k + 1
Else
OutPut(i, j) = ""
End If
Next j
Next i
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' put the data on the sheet
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ExtractUniques = OutPut
End Function
You should return two dimensional arrays: n × 1 for row and 1 × n for column vectors.
So you need either
Redim molPcts(1, Ubound(chems) + 1)
or
Redim molPcts(Ubound(chems) + 1, 1)
To refer to them, you need to use both indices:
molPcts(1, chemNo + 1)
or
molPcts(chemNo + 1, 1)
If you prefer 0-based arrays, the redim should be like this:
Redim molPcts(0 To 0, 0 To Ubound(chems))
Redim molPcts(0 To Ubound(chems), 0 To 0)

Array Comparision

I have a array with 8 columns and 42 rows B2 TO I43. I have to compare this array with other arrays in the same sheet so that every array have same values. I defined array1 Rang('B2;I43") and need to compare every other array of same size. how to that in VBA.
My code is
Sub driver()
Dim array1, array2, m, n
Set array1 = Range("B2,I43")
total_rows = 42
total_cols = 8
Set array2 = Range("B44:I85")
For i = 1 To total_rows
For j = 1 To total_cols
If array1(i, j) = array2(i, j) Then
array2.Cells(i, j).Interior.ColorIndex = 0
ElseIf array1(i, j) <> array2(i, j) Then
array2.Cells(i, j).Interior.ColorIndex = 3
End If
Next j
Next i
End Sub
I want array2 to point to other set of values. Every array start after 42 rows.
Have you tried to use Conditional formatting instead? Your suggested VBA code can easily be solved with conditional formatting by comparing each cell in array2 with the same cell in array1 and use colours to mark if the cells are equal or not
Edit
I have modified your code. Instead of using two ranges I have used an "row offset" for each array you have on your sheet. It then compares the cells from your source array (array1 in your code) with the cells that are found next_array_offset rows down. When the comparison has been made, the offset is increased with 42. The loop ends when there are no more values to be found.
Is this what you was looking for?
Sub driver()
Dim r As Integer
Dim c As Integer
Dim source_row As Integer
Dim source_col As Integer
Dim total_rows As Integer
Dim total_cols As Integer
Dim next_array_offset As Integer
source_row = 2 ' row B
source_col = 2 ' col 2
total_rows = 42
total_cols = 8
next_array_offset = 42 ' distance in rows to next array
Do Until IsEmpty(Cells(source_row + next_array_offset, source_col).Value)
For r = 0 To total_rows - 1
For c = 0 To total_cols - 1
If Cells(source_row + r, source_col + c) = Cells(source_row + next_array_offset + r, source_col + c) Then
Cells(source_row + next_array_offset + r, source_col + c).Interior.ColorIndex = 0
Else
Cells(source_row + next_array_offset + r, source_col + c).Interior.ColorIndex = 3
End If
Next
Next
next_array_offset = next_array_offset + 42
Loop
End Sub
Your main task is to define the ranges accurately. In the code below I've assumed it's every 42 rows until end of data. You simply iterate your 42-row test arrays and compare against the reference array. To do this you basically need two row variables: one for your test array and one for your reference array.
The quickest way would be to read the test data just once into one big array and to create two ranges (one with matches and one with mis-matches) and then colour them at the end of the routine.
I don't know your colour palette (and therefore the color indexes) so I've used the .Color property. You can adjust this to suit.
Const ROW_COUNT As Long = 42
Const COL_COUNT As Long = 8
Const START_ROW As Long = 2
Dim refArray As Variant, testArray As Variant
Dim rowSize As Long, r As Long, c As Long, i As Long
Dim cell As Range, yesRng As Range, noRng As Range
'Read data into arrays
With Sheet1
'Find last row of data
rowSize = .Cells(.Rows.Count, "B").End(xlUp).Row
'Adjust last row to be multiple of 42
rowSize = Int((rowSize - START_ROW) / ROW_COUNT) * ROW_COUNT
refArray = .Cells(START_ROW, "B").Resize(ROW_COUNT, COL_COUNT).Value2
testArray = .Cells(START_ROW + ROW_COUNT, "B").Resize(rowSize, COL_COUNT).Value2
End With
'Compare test array with reference array
i = 1 'refArray row index
For r = 1 To UBound(testArray, 1)
For c = 1 To UBound(testArray, 2)
Set cell = Sheet1.Cells(r + START_ROW + ROW_COUNT - 1, c + 1)
If testArray(r, c) = refArray(i, c) Then
'It's a match so add to yes range
If yesRng Is Nothing Then
Set yesRng = cell
Else
Set yesRng = Union(yesRng, cell)
End If
Else
'It's a miss so add to no range
If noRng Is Nothing Then
Set noRng = cell
Else
Set noRng = Union(noRng, cell)
End If
End If
Next
'Increment ref row index or set back to 1 if at 42
i = IIf(i < ROW_COUNT, i + 1, 1)
Next
'Colour the ranges
If Not yesRng Is Nothing Then yesRng.Interior.Color = vbGreen
If Not noRng Is Nothing Then noRng.Interior.Color = vbRed

Go through values in range, search for them in range, find value in respective rows, add them to array

I would like to go through a range of values in Column D and take each value:
for each value
check in the same range for its occurrence
check in the row of its occurrence for a value in column A
Add this value in column a to an array (or another way to save data)
go to the next occurrence of the value in column D and save the next Value of Column A to the array
When I checked each value for all its occurrences and added it to the array I want the array to be given out in the cell H1 (and for the next values onwards, I1 and so on)
Here's a picture of what I mean with some dummy values:
My attempts in VBA so far are this (with the remark that I deal with arrays for the first time):
Dim finden As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim i As Integer
Dim zahl As Integer
Dim zeile As Range
Dim temparray As Double
Dim b As Integer
Dim count As Integer
Set rng = Worksheets("Tabelle1").Range("H1:H100")
i = Worksheets("Tabelle1").Cells(Rows.count, "D").End(xlUp).Row
For zahl = 1 To i
finden = Sheets("Tabelle1").Cells(zahl, "D").Value
count = Application.WorksheetFunction.CountIf(Range("A1:A100"), finden)
Set zeile = Sheets("Tabelle1").Columns("D").Find(finden, Cells(Rows.count, "D"), xlValues, xlWhole)
If Not zeile Is Nothing Then
FoundCell = zeile.Address
Do
For b = 1 To count
Set temparray(b, 1) = Sheets("Tabelle1").Cells(zeile.Row, "A").Value
Set zeile = Sheets("Tabelle1").Columns("A").Find(finden, zeile, xlValues, xlWhole)
Next b
Loop While zeile.Address <> FoundCell
End If
Set zeile = Nothing
rng.Value = temparray
Sheets("Tabelle1").Cells(1, 8 + zahl) = rng.Value
Next
End Sub
Unfortunately I already get a error message for:
set temparray(b,1)
telling me a data field was expected.
Any idea how I could solve my problem?
Have a look at the Collection object as it is a good way to store unique values. You don't need to run the multiple Find functions or incrementally build your array, you could simply read the columns once and write them into the relevant collection.
It's had to tell from your question and code how you want to write the output, but the code below will set you in the right direction:
Dim uniques As Collection
Dim valueSet As Collection
Dim valueD As String
Dim valueA As String
Dim v As Variant
Dim r As Long
Dim c As Long
Dim output() As String
'Read the data
With ThisWorkbook.Worksheets("Tabelle1")
v = .Range("A1", _
.Cells(Rows.Count, "D").End(xlUp)) _
.Value2
End With
'Populate the collections
Set uniques = New Collection
For r = 1 To UBound(v, 1)
valueA = CStr(v(r, 1))
valueD = CStr(v(r, 4))
'Check if we have a collection for the D value
Set valueSet = Nothing
On Error Resume Next
Set valueSet = uniques(valueD)
On Error GoTo 0
'If not then create a new one.
If valueSet Is Nothing Then
Set valueSet = New Collection
uniques.Add valueSet, Key:=valueD
End If
'Add the A value to it
valueSet.Add valueA
Next
'Compile the write array
ReDim Preserve output(1 To 1, 1 To uniques.Count)
c = 1
For Each valueSet In uniques
For Each v In valueSet
'--> uncomment this 'If block', if you want
'--> comma separated values.
' If Len(output(1, c)) > 0 Then
' output(1, c) = output(1, c) & ", "
' End If
output(1, c) = output(1, c) & v
Next
c = c + 1
Next
'Write the output array
ThisWorkbook.Worksheets("Tabelle1") _
.Range("H1").Resize(, UBound(output, 2)) _
.Value = output

Resources