Iterating through a 2D Array with a for loop - arrays

I'm trying to create an associative table on a sheet that is pulling in data from a different sheet. By associative I mean, if the data is changed in the source data sheet, it would be reflected on the new sheet. I also want to only have the new sheet's table to be contingent on having a certain unique value. In my case, I want to pull up information related to a part number. The original source data will have many rows that contain the same part number, but I only care to display one of them.
So far, this is what I have:
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
Dim bDimen As Byte, i As Long
On Error Resume Next
If IsError(UBound(arr, 2)) Then bDimen = 1 Else bDimen = 2
On Error GoTo 0
Select Case bDimen
Case 1
On Error Resume Next
IsInArray = Application.Match(stringToBeFound, arr, 0)
On Error GoTo 0
Case 2
For i = 1 To UBound(arr, 2)
On Error Resume Next
IsInArray = Application.Match(stringToBeFound,
Application.index(arr, , i), 0)
On Error GoTo 0
If IsInArray = True Then Exit For
Next
End Select
End Function
Sub Part_Separator()
Dim Source As Worksheet
Set Source = Sheets("Part Tracking Scorecard")
Dim ref1(), ref2() As Variant
Dim row, index, lastrow, lastcolumn As Integer
row = 92
lastrow = 866
lastcolumn = 84
ref1 = Source.Range(Source.Cells(row, 1), Source.Cells(lastrow, lastcolumn))
ReDim ref2(UBound(ref1, 1), UBound(ref1, 2))
For index = 0 To (lastrow - row)
If IsInArray(ref1(index, 6).Value, ref2) Then
index = index + 1
Else
ref2(index) = ref1(index) 'copy the entire row from source to ref2
index = index + 1
End If
Next index
Dim NewFile As Worksheet
Set NewFile = Sheets("Unique Parts")
Dim ref2dimension_x, ref2dimension_y As Integer 'find dimensions of ref2 array
ref2dimension_x = UBound(ref2, 1) - LBound(ref2, 1) + 1
ref2dimension_y = UBound(ref2, 2) - LBound(ref2, 2) + 1
For index = 2 To ref2dimension_x 'go through entire new sheet and set values
For index2 = 1 To ref2dimension_y
NewFile.Cells(index, index2).Value = ref2(index - 1, index2)
Next index2
Next index
Erase ref1()
Erase ref2() 'free up the space occupied by these arrays
End Sub
My issue when I run this, I get an error at the first for loop where I'm trying to iterate through my ref1 array, which is the array with all my source data. The error says that my subscript is out of range. This loop is suppose to check if the specific value is in my ref2 array for uniqueness. If the specific value is found, go onto the next row, if not add the row of values associated to the value I'm checking to ref2.

Related

How do I get data in cells as an array up one row in the same column without selecting?

I am trying to write a process that compares strings and deletes the duplicate string within a given column using a selection as the top and bottom constraints.
Most of the process of checking and deleting works however I am having trouble with moving the cell contents up a cell after the duplicate string was deleted.
Image showing how the script should work
Red outline is the loop that selects the String to compare against.
Green outline is the loop that finds, deletes and moves the cells up one.
Blue outline is the Selection.
Stage 1 is to find and compare two strings that are the same.
Stage 2 is to delete the string that is the same as the first string.
Stage 3 is to move everything under the deleted cell with the deleted string up one row so that there is no empty cell.
I'm having problems with stage 3. I don't know how to move all data in those cells up one row without using a loop and I can't use the selection.
Here is the code so far:
Private Sub Tabeller()
Dim vRngMv As Variant
Dim iRowChsr1, iRowChsr2, iRowTtl, iI As Integer
Dim vRowIn, vRowComp As String
Dim oRngSlct, oRngMv As Range: Dim ws As Worksheet: Dim oBS As Object
'Newer Version will get rid of Selection as range determination
'Why does oRngSlct become a Variant/Object/Range here and oRngMv stays a Range object?
'I dont use it, kept it in to ask the question.
Set oRngMv = Selection: Set oRngSlct = Selection
iRowTtl = oRngSlct.Rows.Count
'First Loop For holding target cell data for comparison
For iRowChsr1 = 1 To iRowTtl
'Chooses target cell and string
vRowIn = oRngSlct(iRowChsr1, 1)
'Second loop for Seeking a matching String
For iRowChsr2 = 1 To iRowTtl
'Check to not pick itself
If iRowChsr1 = iRowChsr2 Then
'Offsets Counter by 1 if it enocunters itself
iRowChsr2 = iRowChsr2 + 1
Else
'Sets comparison string
vRowComp = oRngSlct(iRowChsr2, 1)
'String comparison
iI = StrComp(vRowIn, vRowComp, 1)
'If strings are equal
If iI = 0 Then
'Deletes; I know this is redundant but its here for clarity
oRngSlct(iRowChsr2, 1) = ""
'Offsets by iRowChsr by 1
iRowChsr2 = iRowChsr2 + 1
'Create Variant with proper range, it just has to be translated into something that excel can move.
vRngMv = Range((oRngSlct(iRowChsr2, 1)), (oRngSlct(iRowTtl, 1)))
Set oRngMv = Range 'I know this doesnt work
'Offsets back to original Position of Deleted cell
iRowChsr2 = iRowChsr2 - 1
'*******************************
'*Cuts and pastes or moves here*
'*******************************
End If
End If
'Next Comparison String
Next iRowChsr2
'Next target String
Next iRowChsr1
End Sub
Unique (Remove Duplicates)
You could rather use one of the following.
The first solution will leave error values and blanks as part of the resulting data, while the second one will remove them.
The Code
Option Explicit
Sub removeDupesColumnSelection()
' Validate Selection.
If TypeName(Selection) <> "Range" Then Exit Sub
' Remove duplicates.
Selection.Columns(1).RemoveDuplicates Array(1), xlNo
End Sub
Sub uniquifyColumnSelection()
' Validate Selection.
If TypeName(Selection) <> "Range" Then Exit Sub
' Write values from first column of Selection to Data Array.
Dim rg As Range: Set rg = Selection.Columns(1)
Dim rCount As Long: rCount = rg.Rows.Count
Dim Data As Variant
If rCount > 1 Then
Data = rg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
End If
' In Unique Dictionary...
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
' Write unique values from Data Array to Unique Dictionary.
Dim Key As Variant
Dim i As Long
For i = 1 To rCount
Key = Data(i, 1)
If Not IsError(Key) Then
If Len(Key) > 0 Then
.Item(Key) = Empty
End If
End If
Next i
ReDim Data(1 To rCount, 1 To 1)
If .Count > 1 Then
' Write values from Unique Dictionary to Data Array.
i = 0
For Each Key In .Keys
i = i + 1
Data(i, 1) = Key
Next Key
End If
End With
' Write values from Data Array to Destination Range.
rg.Value = Data
End Sub

Compare values of an Array in VBA and don't increment the array or add the value if it is a duplicate

I'm trying to loop through a spread sheet of non consecutive values and read/compare the value to the rest of the values previously read into the array before incrementing the array dimension and adding the value to the array. Ill try to demonstrate with a little example below.
i.e.
Sub ArrayCompare()
Dim Cntry() As String
ArrayDim = 5 'The array is dimensioned with another counter that is not pertinent to this question but typically not greater than 5 in 1 dimension
ReDim Cntry(ArrayDim)
Range("C1").Select
Dim Counter As Integer
Counter = 8 'In the real spread sheet the counter is dynamic, ive just put this in as an example
Do Until Counter = 0
ArrayCounter = 0 'This is used to compare the array values Cntry(C0)
Do Until ActiveCell.Value <> ""
If ActiveCell.Value = "" Then
ActiveCell.Offset(1, 0).Select
Else: End If
Loop
If Active.Value = Cntry(ArrayCounter - 1) Or ActiveCell.Value = Cntry(ArrayCounter - 2) Or ActiveCell.Value = Cntry(ArrayCounter - 3) Or ActiveCell.Value = Cntry(ArrayCounter - 4) Then 'this doesn't work because the array is not dimensioned to this size yet.
ActiveCell.Offset(1, 0).Select
Else
Cntry(ArrayDim) = ActiveCell.Value
ArrayDim = ArrayDim + 1
End If
Counter = Counter - 1
Loop
End Sub
Use Collections, they're are easier to work with. Here's example code where you loop through your collection of stored values and mark a flag if you find a duplicate. The same thing can be done with an array if you insist on using them
Sub Add_Value_If_Not_Duplicate()
Dim My_Stuff As New Collection
Dim End_of_Data, i, t As Integer
Dim Unique_Value As Boolean
End_of_Data = 13 'This will obviously be different for you
Unique_Value = True
Dim New_Value As Integer
'Loops through Column A of sheet 2 to demonstrate the approach
For i = 1 To End_of_Data 'Iterate through the data in your excel sheet
New_Value = Sheet2.Range("A" & i).Value 'Store the new value in a variable
If My_Stuff.Count > 0 Then 'If you have previously read values
'Looping through previously recorded values
For t = 1 To My_Stuff.Count
If My_Stuff(t) = New_Value Then
Unique_Value = False 'If value already exist mark a flag
Exit For
End If
Next
Else
'If you have no previously read values
End If
'Add if Unique
If Unique_Value = True Then 'If value isn't already listed then add it
My_Stuff.Add (New_Value)
End If
Unique_Value = True 'Reset your
Next
For i = 1 To My_Stuff.Count
Sheet2.Range("B" & i) = My_Stuff(i) 'Printing to demonstrate
Next
End Sub

Condensing Excel data with overlapping index/repetitive word occurrence

I have an excel sheet that is formatted like so:
I would like to format it to be something like this:
It is about 40,000 cells of information, so is there any way to do this that isn't manually?
You could probably use =SUMIF to achieve this, since you appear to have numbers as values.
Create a new sheet, copy column A from your data sheet to your new sheet and remove duplicates. Copy row 1 from your data sheet to your new sheet.
Use this formula in sheet 2 cell B2:
=SUMIF(Sheet1!$A:$A;Sheet2!$A2;Sheet1!B:B)
Drag the formula to the right, then down.
I am by no means an excel expert, and this is going to be my first answer ever. Take this into account please.
I've checked it and it works.
I've add a command button in Sheet1 (where the original data is), and when clicked this code writes formatted data into Sheet2.
No need to manually remove duplicates!
Dim dU1 As Object, cU1 As Variant, iU1 As Long, lrU As Long
Dim MyArray() As Variant
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim h As Integer
Private Sub CommandButton1_Click()
'Get unique indexes
Set dU1 = CreateObject("Scripting.Dictionary")
lrU = Cells(Rows.Count, 1).End(xlUp).Row 'number of rows
cU1 = Range("A2:A" & lrU) 'Assuming your data starts in A2
For iU1 = 1 To UBound(cU1, 1)
dU1(cU1(iU1, 1)) = 1
Next iU1
'Now dU1 contains indexes as unique values (about, absence, etc.)
For i = 0 To dU1.Count - 1 'for each index
ReDim MyArray(1 To 1) As Variant 'starts a "new" array
For j = 2 To 9 'each of the columns with values (D1-D8)
a = 0
For k = 2 To lrU 'all rows
If (Worksheets("Sheet1").Cells(k, 1).Value = dU1.keys()(i) And Worksheets("Sheet1").Cells(k, j).Value <> "") Then
MyArray(UBound(MyArray)) = Worksheets("Sheet1").Cells(k, j).Value 'add value to array
ReDim Preserve MyArray(1 To UBound(MyArray) + 1) As Variant 'resize array (now is 1 element longer)
a = a + 1
End If
Next
If a = 0 Then 'if no value found, add an element to array anyway
MyArray(UBound(MyArray)) = "" 'add value to array
ReDim Preserve MyArray(1 To UBound(MyArray) + 1) As Variant 'resize array (now is 1 element longer)
End If
Next
Worksheets("Sheet2").Cells(i + 2, 1) = dU1.keys()(i) 'write indexes in another sheet
For h = 2 To UBound(MyArray)
Worksheets("Sheet2").Cells(i + 2, h) = MyArray(h - 1)
Next
Next
End Sub

Dynamic row and static column array for 'write line' loop

I need to generate an array for a 'write line' custom function. This function writes a single row of cells (the array) to a text file during a loop.
This array is static in size.
The row to be written to the text file is 'detected' by the Rng variable below:
Set Rng = Worksheets("Sheet1").Columns(5).Find(userform1.ComboBox1.Value)
i.e. the Rng variable only indexes a single cell in column 5 - as matched by the value in ComboBox1. The array to be written by writeLine will be column 6:49 of the row matched by Rng.
The arguments for the 'write line' function are
the value of the combobox i.e. userform1.combobox1.value, and
the array to be written in the 'write line' function
So far, I have this:
Private Sub CommandButton18_Click()
Dim Range As Range
Dim Array As Range
Set Range = Worksheets("Sheet1").Columns(5).Find(userform1.ComboBox1.Value) ''anticipated to set the row index of the array???
Array = ????
Function.writeLine(userform1.ComboBox1.Value,????)
End Sub
The trouble I am having is to create an array that incorporates the changing row whilst having the number of columns fixed?
Here is part of the writeLine function:
Do Until objTF.AtEndOfStream
readString = objTF.readline
data = Split(readString, vbTab)
foundID = data(0)
If StrComp(**foundID, ID**, 1) <> 0 Then
objTF2.writeLine (readString)
ElseIf StrComp(**foundID, ID**, 1) = 0 Then
'write initial value outside the loop
strTmp = Split(readString, strDelim)
'Modify the data array to include the data provided by writeArray
For argPos = 5 To UBound(data)
'check for index out of bounds, stop writing if it is!
If (argPos - 5) > UBound(writeArray) Then Exit For 'need to check this will exit if the value is index out of bounds.
data(argPos) = writeArray(dataPos)
dataPos = dataPos + 1
Next argPos
'Take each entry from data and build a string delimited by strDelim
Do Until counter > UBound(data)
resultStr = IIf(counter <= UBound(data), resultStr & data(counter) & strDelim, resultStr & data(counter))
counter = counter + 1
Loop
'output to temp file
objTF2.writeLine (resultStr)...
Basically, the "foundID" and "ID" are the variables to be matched in order to write the array to the text file. The "ID" is the ComboBox1.value. "foundID" is the value in column 5 of the spreadsheet. Column 6:49 (the array) are to be written to the text file.
Use Ubound to get the range as array.
Private Sub CommandButton18_Click()
Dim rRange As Range
Dim colno As Long
Dim rownumber As Long
Dim sString As String
lastrow = 10
For colno = 6 To 43
For rownumber = 1 To lastrow
sString = Worksheets("sheet1").Cells(rownumber, colno)
Next rownumber
Next
End Sub

Copy a range of cells, defined on execution, to an array

I am trying to copy a range of cells from a range of rows from two workbooks. This information is used to do a comparison of the contents of both workbooks rows by ID.
The first solution I tried involved cell by cell "binary" comparison. This works for worksheets with few rows:
For i = 2 To LastSheetRow
Set FoundCell = Workbooks(WorkbookA).Sheets(SheetNameFromArray).Range("A:A").Find(What:=Workbooks(WorkbookB).Sheets(SheetNameFromArray).Cells(i, 1).Value)
If Not FoundCell Is Nothing Then
aCellValues(0) = 1
Workbooks(UserWorkbook).Sheets(SheetNameFromArray).Cells(i, LastSheetColumn + 1).Value = FoundCell.Row
For j = 2 To LastSheetColumn
Select Case Workbooks(WorkbookB).Sheets(SheetNameFromArray).Cells(i, j).Value
Case Is = Workbooks(WorkbookA).Sheets(SheetNameFromArray).Cells(FoundCell.Row, j).Value
aCellValues(j - 1) = 1
Case Else
aCellValues(j - 1) = 0
End Select
Next j
Else
End If
Next i
I would like to store the contents of one row of each of the two workbooks on one array to do the comparison, as I believe it's a faster solution.
After defining the range to do the comparison I encountered the following error when copying the cells into an array:
Subindex out of interval (Error 9)
This generates the error:
Dim aWorkbookBInfo() As Variant, aWorkbookAInfo() As Variant, rngWorkbookBToCompare As Range, rngWorkbookAToCompare As Range
Dim SumToCheck As Integer, FoundCell As Range, aCellValues() As Integer
ReDim aCellValues(LastSheetColumn - 1)
ReDim aWorkbookBInfo(LastSheetColumn - 1)
ReDim aWorkbookAInfo(LastSheetColumn - 1)
For i = 2 To LastSheetRow
Set FoundCell = Workbooks(WorkbookA).Sheets(SheetNameFromArray).Range("A:A").Find(What:=Workbooks(WorkbookB).Sheets(SheetNameFromArray).Cells(i, 1).Value)
If Not FoundCell Is Nothing Then
aCellValues(0) = 1
Workbooks(WorkbookB).Sheets(SheetNameFromArray).Cells(i, LastSheetColumn + 1).Value = FoundCell.Row
With Workbooks(WorkbookB).Sheets(SheetNameFromArray)
Set rngWorkbookBToCompare = Range(Cells(i, 2), Cells(i, LastSheetColumn))
End With
With Workbooks(WorkbookA).Sheets(SheetNameFromArray)
Set rngWorkbookAToCompare = Range(Cells(FoundCell.Row, 2), Cells(FoundCell.Row, LastSheetColumn))
End With
aWorkbookBInfo = rngWorkbookBToCompare
aWorkbookAInfo = rngWorkbookAToCompare
For j = 1 To LastSheetColumn - 1
If aWorkbookBInfo(j).Value = aWorkbookAInfo(j).Value Then
aCellValues(j) = 1
Else
aCellValues(j) = 0
End If
Next j
Else
End If
Next i
Complete Revision:
The range array assignment produces a two-dimensional array in these lines:
aWorkbookBInfo = rngWorkbookBToCompare
aWorkbookAInfo = rngWorkbookAToCompare
This happens regardless of how you defined and dimensioned them at the beginning of your code. Since they are a two-dimensional array, they must be addressed as aWorkbookBInfo(a, b) where a is a row and b is a column.
Unlike Ranges, where it is okay to reference the first cell in any range, you must fully address an array before attempting to reference the array item. So, while rngWorkbookBToCompare(j).Value works, aWorkbookBInfo(j).Value does not. Furthermore, Value is not necessarily a property of whatever object Excel puts in the array. If you want the first cell of column j, try adding the row and leaving off the reference to the Value property as in: aWorkbookBInfo(1, j).

Resources