I have the following function to run on a large excel ark with 60k rows:
Private Sub mySub()
Dim intRowA As Long
Dim intRowB As Long
Application.ScreenUpdating = False
Range("W1").EntireColumn.Insert
For intRowA = 2 To ActiveSheet.UsedRange.Rows.Count
If Cells(intRowA, 6).Value = "C" Then
For intRowB = 2 To ActiveSheet.UsedRange.Rows.Count
If Cells(intRowB, 6).Value = "P" Then
If Cells(intRowA, 4).Value = Cells(intRowB, 4).Value And Cells(intRowA, 7).Value = Cells(intRowB, 7).Value Then
Cells(intRowA, 23).Value = "Matched"
Cells(intRowB, 23).Value = "Matched"
End If
End If
DoEvents
Next
End If
Next
For intRowA = ActiveSheet.UsedRange.Rows.Count To 2 Step -1
If Cells(intRowA, 23).Value <> "Matched" Then
Rows(intRowA).Delete shift:=xlShiftUp
End If
Next
Range("W1").EntireColumn.Delete
Application.ScreenUpdating = True
End Sub
The idea to check where F columns are C and match them up with all F
Rows that are value P Then at the end Delete all that does not match
The problem with this code as far as i can see is that it runs the 60k rows 60K times. which makes my script crash. i am unsure how to improve it and thought that you guys might be able to see through this?
You're coming at this problem from the wrong direction - what makes a row distinct isn't whether column F has a 'C' or a 'P', it's whether the values in columns 'D' and 'G' match.
The way to approach this is to collect 2 lists of rows with every distinct combination of 'D' and 'G' - one for rows with a 'C' in column F and one for rows with a 'P' in column F. Then, go through all of the distinct values for the 'C's and match based on the distinct combination. Something like this (requires a reference to Microsoft Scripting Runtime):
Private Sub mySub()
Dim sheet As Worksheet
Dim c_rows As Dictionary
Dim p_rows As Dictionary
Set sheet = ActiveSheet
Set c_rows = New Dictionary
Set p_rows = New Dictionary
Dim current As Long
Dim key As Variant
'Collect all of the data based on keys of columns 'D' and 'G'
For current = 2 To sheet.UsedRange.Rows.Count
key = sheet.Cells(current, 4) & vbTab & sheet.Cells(current, 7)
'Stuff the row in the appropriate dictionary based on column 'F'
If sheet.Cells(current, 6).Value = "C" Then
If Not c_rows.Exists(key) Then
c_rows.Add key, New Collection
End If
c_rows.Item(key).Add current
ElseIf sheet.Cells(current, 6).Value = "P" Then
If Not p_rows.Exists(key) Then
p_rows.Add key, New Collection
End If
p_rows.Item(key).Add current
End If
Next current
sheet.Range("W1").EntireColumn.Insert
'Now filter out the matching Ps that have keys in the C Dictionary:
For Each key In c_rows.Keys
If p_rows.Exists(key) Then
Dim match As Variant
For Each match In p_rows(key)
sheet.Cells(match, 23).Value = "Matched"
Next
End If
Next key
For current = sheet.UsedRange.Rows.Count To 2 Step -1
If sheet.Cells(current, 23).Value = "Matched" Then
sheet.Rows(current).Delete xlShiftUp
End If
Next
sheet.Range("W1").EntireColumn.Delete
End Sub
I agree it is the 60k x 60k loop causing the issue. You can make the loop more efficient a few different ways:
1) Run through the loop and delete all rows where column F doesn't equal C or P beforehand. This may solve the issue outright if there aren't that many rows that contain C or P.
2) Loop through all the rows once and store the necessary row numbers in an array or collection. Then do whatever you need done with the rows separately. For example:
Dim intRow As Long
Dim cCollection As New Collection
Dim pCollection As New Collection
For intRow = 2 To ActiveSheet.UsedRange.Rows.Count
If Cells(intRow, 6).Value = "C" Then
cCollection.Add (intRow)
ElseIf Cells(intRow, 6).Value = "P" Then
pCollection.Add (intRow)
End If
Next
Dim i As Integer
For i = 1 To cCollection.Count
' do something with cCollection(i)
Next
' multiple ways to loop through the collection...
Dim r As Variant
For Each r In pCollection
'do something with pCollection(r)
Next r
Related
The script fills an array from a sheet called "Tigers" with 6 strings. Then it is supposed to compare that array to a differnt sheet titled "Elephants" and tell me if it finds an exact match. The troublesome code is found at the Application.Match method
Any help understanding how to correctly script a match with multiple values would be appreciated.
Sub matchData()
Dim arrCompare(5) As Variant
Dim intRow As Integer
Dim varRes As Variant
Set sht = ActiveSheet
Set shtTigers = Worksheets("Tigers").Range("A2:A100")
Set shtElephants = Worksheets("Elephants").Range("A2:A100")
Sheets("Elephants").Activate
For intRow = 2 To 100
arrCompare(0) = Worksheets("Elephants").Cells(intRow, 1).Value
arrCompare(1) = Worksheets("Elephants").Cells(intRow, 2).Value
arrCompare(2) = Worksheets("Elephants").Cells(intRow, 4).Value
arrCompare(3) = Worksheets("Elephants").Cells(intRow, 5).Value
arrCompare(4) = Worksheets("Elephants").Cells(intRow, 7).Value
arrCompare(5) = Worksheets("Elephants").Cells(intRow, 9).Value
'compare all 6 strings in array against Elephant sheet rows for a match
varRes = Application.Match(arrCompare(), shtTigers, 0)
'also tried
'varRes = Application.Match(((arrCompare(0))*((arrCompare(1))*((arrCompare(2)) * ((arrCompare(3)) * ((arrCompare(4)) * ((arrCompare(5))*((arrCompare(6)),shtTigers, 0)
'messagebox just gives a Error 13 or 2042 for varRes
MsgBox ("varRes = " & varRes)
Next
End Sub
Match requires a single lookup value but you're trying to pass the whole array. Iterate one element at at time instead:
Dim counter as Integer
For x = 0 to 5
If Not IsError(Application.Match(arrCompare(x), shtTigers, 0)) Then
counter = counter + 1
End If
Next x
If counter = 6 Then Debug.Print "Matches found"
I am a novice when it comes to Excel VBA and Macros. I have a workbook that contains two primary sheets - "DAILY_SHOP_FILE" and "Reconciled", the former serves as an order sheet and the latter serves as an archive sheet for the orders once they have been shipped. I want to write a VBA Script/Macro that transfers an entire row from the DAILY_SHOP_FILE to the Reconciled sheet when a user inputs the value "yes" into the final column. Both sheets will have the same headers in row 1. I found a code on here and modified it slightly to my needs:
Dim keyColumn As Integer
Dim i As Integer
Dim keyWord As Variant 'I've used variant, so you can choose your own data type for the keyword
Dim dataSh As String 'I'm using sheet names for sheet referencing
Dim populateSh As String
Dim rowNum As Integer
Dim dataRow() As Variant
Sub Populate()
'set the column number, which contains the keywords, the keyword itself,
'name of the sheet to populate and the row offset you'd like to start populating
populateSh = "Reconciled"
keyColumn = 15
keyWord = "yes"
rowNum = 1
'assuming you run the macro in the sheet you get the data from, get its name to return to it after copying the row
dataSh = ActiveSheet.Name
'loop through all the used cells in the column
For i = 1 To ActiveSheet.UsedRange.Rows.Count
If Cells(i, keyColumn) = keyWord Then
'starting in row 1 in the sheet you populate, you'll have to set the rowNum variable to desired offset few lines above
rowNum = rowNum + 1
Call copyRow(i, rowNum)
End If
Next i
End Sub
Sub copyRow(ByVal cRow As Integer, ByVal pRow As Integer)
Dim colNum As Integer
'set the number of columns you'd like to copy
colNum = 15
'redimension the array to carry the data to other sheet
'this can be done any way you,d like, but I'm using array for flexibility
ReDim dataRow(1 To colNum)
'put the data into the array, as an example I'm using columns 1-15 while skipping the keyword column.
dataRow(1) = Cells(cRow, 1)
dataRow(2) = Cells(cRow, 2)
dataRow(3) = Cells(cRow, 3)
dataRow(4) = Cells(cRow, 4)
dataRow(5) = Cells(cRow, 5)
dataRow(6) = Cells(cRow, 6)
dataRow(7) = Cells(cRow, 7)
dataRow(8) = Cells(cRow, 8)
dataRow(9) = Cells(cRow, 9)
dataRow(10) = Cells(cRow, 10)
dataRow(11) = Cells(cRow, 11)
dataRow(12) = Cells(cRow, 12)
dataRow(13) = Cells(cRow, 13)
dataRow(14) = Cells(cRow, 14)
dataRow(15) = Cells(cRow, 15)
Sheets(populateSh).Select
For p = 1 To UBound(dataRow)
Cells(pRow, p) = dataRow(p)
Next p
Sheets(dataSh).Select
End Sub
It works well but the only problem is it doesn't actually delete the row from the DAILY_SHOP_FILE. How could I solve this? Additionally, it'd be nice to refer to the sheetnames as per the VBA rather than the actual tab names because if a user renamed one of the tabs the code wouldn't work anymore. Thank You!
Sub Update_Reconciled()
Application.ScreenUpdating = False
Dim T2()
Set D1 = CreateObject("scripting.dictionary")
Set R1 = Sheet1.UsedRange 'update Sheet1 to match DAILY_SHOP_FILE code name
T1 = R1
a = 1
For i = 2 To UBound(T1)
If Trim(UCase(T1(i, UBound(T1, 2)))) = "YES" Then
D1(i) = i
ReDim Preserve T2(1 To UBound(T1, 2), 1 To a)
For j = 1 To UBound(T1, 2)
T2(j, a) = T1(i, j)
Next j
a = a + 1
End If
Next i
If a > 1 Then
Sheet2.Range("A99999").End(xlUp).Offset(1, 0).Resize(UBound(T2, 2), UBound(T2, 1)) = Application.Transpose(T2) 'update Sheet2 to match Reconciled code name
cnt = 0
For Each k In D1.items
Sheet1.Rows(k - cnt).Delete 'update Sheet1 to match DAILY_SHOP_FILE code name
cnt = cnt + 1
Next k
End If
Application.ScreenUpdating = True
End Sub
Sorry for not looking at your specific setup, but here is a generic solution that should work fine for you, with just a bit of customization. This is general enough to help others as well.
Sub NewSheetData()
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Dim Rng As Range
Set Rng = Range([A1], Range("A" & Rows.Count).End(xlUp))
On Error Resume Next
With Rng
.AutoFilter , field:=1, Criteria1:="network", Operator:=xlOr, Criteria2:="telcom"
.SpecialCells(xlCellTypeVisible).EntireRow.Copy Sheets("Sheet2").Range("A1")
.AutoFilter
End With
On Error GoTo 0
Application.EnableEvents = True
End Sub
I've what seems like a pretty simple application with looping and 'If..Then' statements but need some help on structuring it.
In very a basic example, I have a list numbers in column A and the values PM or AM listed in column B. I want to write a loop that will search every value in column B until the end of the data set, and add 12 to each value in column A each time column B has a value of PM. In a nutshell, it would look like this:
If column B = PM
then add 12 to its corresponding cell in column A
else move down to the next row and do the same thing until you reach an empty cell
There are many ways, here is a typical one:
Sub dural()
Dim i As Long
i = 1
Do While Cells(i, "B").Value <> ""
If Cells(i, "B").Value = "PM" Then
Cells(i, "A").Value = Cells(i, "A").Value + 12
End If
i = i + 1
Loop
End Sub
you can set it with For next loop and 2 variables. one for last row and the 2nd for the row count:
Sub Macro1()
Dim LastRow As String
Dim i As Integer
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To LastRow
If Cells(i, 2).Value = "PM" Then Cells(i, 1).vlaue = Cells(i, 1).vlaue + 10
Next i
End
'
End Sub
This is another way to do this.
Option Explicit
Sub Add()
Dim rData As Range
Dim r As Range
Set rData = Cells(1, 1).CurrentRegion.Columns("B").Cells
For Each r In rData
If UCase$(r.Value) = "PM" Then
r.Offset(, -1).Value = r.Offset(, -1).Value + 12
End If
Next r
End Sub
I want to copy values from one column to another column (into the same row) if the cell contains the word IN and delete the original value. If not, the code should proceed to the next row and perform a new test. Thus the cell in the target column will remain empty.
When I run the code in Excel nothing happens, so I don't know what is wrong.
Ideally the code should jump to the next column (8) and do the same search and paste the value into the same column (5) when it is done with the first column, but this I haven't started with yet. So I do appreciate tips for that as well :)
Sub Size()
Dim i As Integer, a As String
i = 2
a = "IN"
Do While Cells(i, 7).Value <> ""
If InStr(Cells(i, 7), a) Then
'copying the value to another column but within the same row
Cells(i, 7).Copy Cells(i, 5)
Cells(i, 7).Clear
i = i + 1
Else
i = i + 1
End If
Loop
End Sub
I found out that my first cell in column 7 was empty and thus the Do While Cells(i, 7).Value <> "" wasn't working. Hence I'm refering to a different column that always contain data. Note that the solution code also jumps to the 2 next columns in order to search for the same word.
Sub Size()
Dim i As Integer, a As String
j = 0
i = 1
a = "IN"
Range("A1").Offset(i, 0).Select
For j = 0 To 2
Do Until Selection.Value = ""
If InStr(Range("G1").Offset(i, j).Value, a) Then
Range("E1").Offset(i, 0).Value = Range("G1").Offset(i, j).Value
Range("G1").Offset(i, j).Clear
i = i + 1
Range("A1").Offset(i, 0).Select
Else
i = i + 1
Range("A1").Offset(i, 0).Select
End If
Loop
i = 1
Range("A1").Offset(i, 0).Select
Next j
End Sub
I have a workbook that is organized within a main sheet. Every item has 3 rows. These items are grouped and sub-grouped by row and columns.
I have developed several reporting options. These reports identify certain items based upon characteristics in the main sheet and copy them over to another sheet. So far, so good.
My final task would appear simple and based upon prior logic I developed. I need a pop-up window that prompts the user for a column. Based upon the column input, I grab all rows that are not empty (in their corresponding groups of 3) and copy them over. As I indicated, this logic worked previously. I leave a blank row between the groups for easy reading.
I take the column input and translate to column number (thanks to you and a previous post!). The problem is that the code copies over the groups correctly (with non-blank entries), and then once it leaves the first row grouping, it starts copying over non-blank entries.
I know what the entries will be in these columns and also tried using a key method - converting the known entries to ascii and checking cell value against that. Still, the same result.
I am wondering if the problem is the fact that the code resides in the userform? Do I need to separate the userform from the macro? Is columnNumber somehow getting overwritten (it appears that way). There may be artifacts (unused variables) from previous versions and troublshooting...
I grant this is not the most elegant coding I've done, but I am running out of time (I only have a few days left for this entire project). Here it is, and ANY advice or help is greatly appreciated. THANK YOU well in advance :)
Private Sub Cancel_Click()
UserForm4.Hide
End Sub
Private Sub Go_Click()
Dim Test As String
Dim colNumber, columnNumber As Integer
Dim m As Integer
Dim ws2 As String
Dim i, j, k, r As Integer
Dim BlankRow2
Dim ColorCode As Integer
Dim RqtRow As Integer
Dim Item As Integer
Dim ColVal, AscCol As String
Dim Row1Value, Row2Value, Row3Value As Integer
' Initialize Variables
ws1 = "Requirements_Matrix"
ws2 = "OUTPUT"
RqtRow = 8
BlankRow2 = 4
Item = BlankRow2
Lastrow1 = Sheets(ws1).Cells(Rows.Count, "A").End(xlUp).Row
Lastcol1 = Sheets(ws1).Cells(1, Columns.Count).End(xlToLeft).Column
Lastrow2 = Sheets(ws2).Cells(Rows.Count, "A").End(xlUp).Row
Lastcol2 = Sheets(ws2).Cells(1, Columns.Count).End(xlToLeft).Column
Test = UserForm4.WhichTest.Value
If Test <> "" Then
colLetter = UCase(Test)
colNumber = 0
For m = 1 To Len(colLetter)
colNumber = colNumber + (Asc(Mid(colLetter, Len(colLetter) - m + 1, 1)) - 64) * 26 ^ (m - 1)
Next
columnNumber = colNumber
If (columnNumber < 24) Or (columnNumber > 136) Then
UserForm5.Show 'outside test columns - do not have time to execute further error testing...
Else 'Copy requirements from Requirements_Matrix Sheet to Output Sheet
With Sheets(ws2)
Sheets(ws2).Select
Rows("4:5000").Select
Selection.Delete Shift:=xlUp
End With
Sheets(ws1).Select
For i = 8 To Lastrow1 'find non-empty cells
If Sheets(ws1).Cells(i, 3).Interior.ColorIndex = 34 Then
Row3Value = Sheets(ws1).Cells(i, 3).Value
End If
If Sheets(ws1).Cells(i, 2).Interior.ColorIndex = 44 Then
Row2Value = Sheets(ws1).Cells(i, 2).Value
End If
If Sheets(ws1).Cells(i, 1).Interior.ColorIndex = 37 Then
Row1Value = Sheets(ws1).Cells(i, 1).Value
End If
If Sheets(ws1).Cells(i, 5) = "Requirement" Then 'Requirement Row
RqtRow = i
End If
If (Sheets(ws1).Cells(i, columnNumber).Value <> Empty) And _
Sheets(ws1).Cells(i, 3).Interior.ColorIndex <> 34 And _
Sheets(ws1).Cells(i, 2).Interior.ColorIndex <> 44 And _
Sheets(ws1).Cells(i, 1).Interior.ColorIndex <> 37 Then
k = RqtRow + 2
Increment = BlankRow2 + 2
Sheets(ws1).Select
Rows(RqtRow & ":" & k).Select 'select requirement block containing non-blank cell
Selection.Copy
Sheets(ws2).Select
Range(BlankRow2 & ":" & Increment).Select
ActiveSheet.Paste
ActiveSheet.Cells(BlankRow2, 1).Value = Row1Value
ActiveSheet.Cells(BlankRow2, 2).Value = Row2Value
ActiveSheet.Cells(BlankRow2, 3).Value = Row3Value
BlankRow2 = Increment + 2 'leave a blank row between requirements
End If
Next
End If
Else
UserForm5.Show
End If
UserForm4.WhichTest.Value = Empty
UserForm4.Hide
End Sub