I have a workbook where I want to delete all rows in Sheet "Paste MyStock" column A that don't contain a date and then paste a formula and autofill to the last row.
The code works, however, it won't go through all rows. It suddenly stops after 2-3 rows and I have to run the macro several times. Why is this and how do I fix it?
This is my code:
Sub del_row_not_date()
Dim i As Integer
Dim MyStock As Worksheet
Dim Pivot As Worksheet
Dim Dta As Worksheet
Set MyStock = Sheets("Paste MyStock")
Set Formula = MyStock.Range("O1")
Set PasteFormula = MyStock.Range("N2")
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
LastRow = MyStock.Cells(MyStock.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
If IsDate(MyStock.Cells(i, 1)) = False Then
MyStock.Cells(i, 1).EntireRow.Delete
End If
Next
Formula.Copy
PasteFormula.PasteSpecial xlPasteAll
PasteFormula.AutoFill Destination:=MyStock.Range("N2:N" & LastRow)
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
When deleting, loop from the bottom up, otherwise your loop will inadvertently skip rows after deleting:
For i = LastRow to 2 Step -1
Also use Long instead of Integer; see this question why:
Dim i as Long
Related
I'm just starting with VBA and I'm trying to play around a bit to see what I can do.
I'm trying to write a macro that auto-generates a report from a subsection of data on a main worksheet.
I want to only copy rows where the value in Column D is "China" AND the value in Column H is "HS". Also I'm only looking to copy data from a selection of the rows (A:C,E,F,G,I,Q,R,AF:AH,AN,AP,AQ).
So far I'm doing this by:
Creating a new sheet
Copying the title row
Searching for relevant data and copy/pasting into the new sheet
By following a few answers I found here and other forums, I put together the following. The top half works just fine (generating the sheet and copying the title row) but the main important part doesn't.
Forgive me if this is a Frankenstein job, I'm new here but trying to learn!
Option Explicit
Sub GenerateHSReport()
'Generating the sheet'
Sheets.Add(Count:=1).Name = "HS Report " & Format(Date, "DD-MM-YY")
'Adding the title row'
Sheets("SANBI - all bids").Range("A4:C4,E4,F4,G4,I4,Q4,R4,AF4:AH4,AN4,AP4,AQ4").Copy
Sheets("HS Report " & Format(Date, "DD-MM-YY")).Activate
Range("A1").Select
ActiveSheet.Paste
'Copying the HS data'
Dim srchtrm As String
Dim rng As Range, destRow As Long
Dim shtSrc As Worksheet, shtDest As Worksheet
Dim c As Range
Dim i As Integer
With Application
.ScreenUpdating = False
.DisplayStatusBar = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
Set shtSrc = Sheets("SANBI - all bids")
Set shtDest = Sheets("HS Report " & Format(Date, "DD-MM-YY"))
Set c = Range("A5:C5,E5,F5,G5,I5,Q5,R5,AF5:AH5,AN5,AP5,AQ5")
destRow = 2
Set rng = Application.Intersect(shtSrc.Range("D:D, H:H"), shtSrc.UsedRange)
For Each c In rng.Cells
If c.Value = "HS" And c.Value = "China" Then
c.Copy shtDest.Cells(destRow, 2)
destRow = destRow + 1
End If
Next
With Application
.ScreenUpdating = True
.DisplayStatusBar = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
Application.CutCopyMode = False
End Sub
Update
Thanks commenters, it works! How exciting! FYI i had to add a line per each column I wanted to copy, as per below. Maybe it's a bit messy but it seems to work!
shtDest.Cells(destRow, 1).Value = Row.Columns("a").Value
shtDest.Cells(destRow, 2).Value = Row.Columns("b").Value
shtDest.Cells(destRow, 3).Value = Row.Columns("c").Value
'...etc'
I'm on my phone so, I can'tvtest right now, but it should direct you in the right direction
Set rng = shtSrc.UsedRange
Dim row as range
For Each row In rng.rows
If row.columns("h").value = "HS" And row.columns("d").value = "China" Then
shtDest.Cells(destRow, 2).value = Row.columns("b").value
destRow = destRow + 1
End If
Next
Domething like that, you get the idea, again, it's not tested
I am running the following code on a spreadsheet:
Do While i <= 100000
If Not Cells(i, 4) = "String" Then
Cells(i, 4).EntireRow.Delete
End If
i = i + 1
Loop
There are plenty of entries with not "String" but they do not get deleted.
When I copy this piece of code to a separate sheet, I even get the error "Excel cannot complete this task with available resources. Choose less data or close other applications."
What am I doing wrong that is making this loop not work?
Note: I can't use autofilter because I need to delete rows based on not meeting a condition.
This is the worst way to delete a row. Reasons
You are deleting the rows in a Loop
Your Cells Object are not qualified
Try this.
Co-incidentally I answered a similar question in the MSDN forum as well. Please See THIS
Try this way (UNTESTED)
In the below code I have hardcoded the last row to 100000 unlike as done in the above link.
Sub Sample()
Dim ws As Worksheet
Dim i As Long
Dim delRange As Range
'~~> Set this to the relevant worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
For i = 1 To 100000
If .Cells(i, 4).Value <> "String" Then
If delRange Is Nothing Then
Set delRange = .Rows(i)
Else
Set delRange = Union(delRange, .Rows(i))
End If
End If
Next i
If Not delRange Is Nothing Then delRange.Delete
End With
End Sub
NOTE: I am assuming that a cell will have values like
String
aaa
bbb
ccc
String
If you have scenarios where the "String" can be in different cases or in between other strings for example
String
aaa
STRING
ccc
dddStringddd
then you will have to take a slightly different approach as shown in that link.
Autofilter code:
Sub QuickCull()
Dim rng1 As Range
Set rng1 = Range([d4], Cells(Rows.Count, "D").End(xlUp))
ActiveSheet.AutoFilterMode = False
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
With rng1
.AutoFilter Field:=1, Criteria1:="<>string"
If rng1.SpecialCells(xlCellTypeVisible).Count > 1 Then _
.Offset(1, 0).Resize(rng1.Rows.Count - 1).Rows.Delete
End With
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
ActiveSheet.AutoFilterMode = False
End Sub
When you want to delete rows its always better to delete from bottom.
Sub DeleteData()
Dim r As Long
Dim Rng As Range
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ThisWorkbook.Sheets("sheet1")
Set Rng = .Range(.Range("D1"), .Range("D1").End(xlDown))
For r = Rng.Rows.Count To 1 Step -1
If LCase(Trim(.Cells(r, 4).Value)) <> LCase("string") Then
.Cells(r, 4).EntireRow.Delete
End If
Next
End With
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
This is a basic algorithm mistake.
Imagine your program are on, say, row 10. You delete it. So, row 11 becomes row 10, row 12 becomes 11 and so on. Then you go to row 11, skipping row 10, previous row 11!
This would work:
Do While i <= 100000
If Not Cells(i, 4) = "String" Then
Cells(i, 4).EntireRow.Delete
Else
i = i + 1
End If
Loop
I am working on one scenario where I have two sheets. Sheet1 is the master sheet and sheet2 which I am creating.
Column1 of Sheet1 is Object which has duplicate objects as well. So, what I have done is I have created a macro which will produce the unique Objects and will paste it in sheet2.
Now, from Sheet2, each of the objects should be matched with Sheet1 column1 and based on the matching results, it should also count the corresponding entries from other columns in sheet1 to sheet2.
Below are the snapshots of my two sheets
Sheet1
Sheet2
here is my macro code which will first copy and paste the unique objects from sheet1 to sheet2 Column1.
Sub UniqueObj()
Dim Sh1 As Worksheet
Dim Rng As Range
Dim Sh2 As Worksheet
Set Sh1 = Worksheets("Sheet1")
Set Rng = Sh1.Range("A1:A" & Sh1.Range("A65536").End(xlUp).Row)
Set Sh2 = Worksheets("Sheet1")
Rng.Cells(1, 1).Copy Sh2.Cells(1, 1)
Rng.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Sh2.Range("A1"), Unique:=True
End Sub
But, I am unable to move forward from there. I am pretty new and any help would be very greatful.
Thanks
If I'm understanding what you want correctly, you're just counting matching columns from Sheet1 where the value in the corresponding column isn't blank? If so this should do the trick.
Option Explicit
Sub GetStuffFromSheet1()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim lastRow1 As Long, lastRow2 As Long
Dim x As Long
'turn on error handling
On Error GoTo error_handler
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("Sheet2")
'determine last row with data in sheet 1
lastRow1 = ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row
'determine last row with data in sheet 2
lastRow2 = ws2.Cells(ws2.Rows.Count, 1).End(xlUp).Row
'define columns in sheet 1
Const objCol1 As Long = 1
Const rProdCol1 As Long = 3
Const keysCol1 As Long = 4
Const addKeysCol1 As Long = 5
'define columns in sheet 2
Const objCol2 As Long = 1
Const rProdCol2 As Long = 2
Const keysCol2 As Long = 3
Const addKeysCol2 As Long = 4
'turn off screen updating + calculation for speed
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'loop through all rows of sheet 2
For x = 2 To lastRow2
'formula counts # of cells with matching obj where value isn't blank
ws2.Cells(x, rProdCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(rProdCol1), "<>" & "")
ws2.Cells(x, keysCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(keysCol1), "<>" & "")
ws2.Cells(x, addKeysCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(addKeysCol1), "<>" & "")
Next x
'turn screen updating + calculation back on
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
error_handler:
'display error message
MsgBox "Error # " & Err.Number & " - " & Err.Description, vbCritical, "Error"
'turn screen updating + calculation back on
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
End Sub
In case a non VBA solution works for you, you can resume your data with a Pivot Table, take field Object into rows section and rest of fields into values section (choose Count)
This returns the exact output you are looking for. Easy to update and easy to create.
In case you want a VBA solution, because your design is tabular and you are counting values, you can use CONSOLIDATE:
Consolidate data in multiple worksheets
'change K1 with cell where to paste data.
Range("K1").Consolidate Range("A1").CurrentRegion.Address(True, True, xlR1C1, True), xlCount, True, True, False
'we delete column relation type and column value. This columns depends on where you paste data, in this case, K1
Range("L:L,P:P").Delete Shift:=xlToLeft
After executing code i get this:
Hope this helps
i need some advice regarding this macro.
This macro cuts and copies from "LATURAP" sheet, rows if specific conditions are met. exmpl. starts with number 170889 and so on.
Problem is that, when i run this macro, it will only works once when i have imported this to excel.
Can somebody explain what i'm missing here?
Sub Laturap()
Dim i As Integer
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
a = Worksheets("LATURAP").Cells(Rows.Count, "A").End(xlUp).Row
For i = 3 To a
'selection from LATURAP to 1708
If Left(Range("A" & i), 6) = 170889
Then
Worksheets("LATURAP").Range("A:J").Rows(i).Cut
Worksheets("1708").Activate
b = Worksheets("1708").Cells(Rows.Count, "A").End(xlUp).Row
Worksheets("1708").Cells(b + 1, 1).Select
Worksheets("1708").Paste
Worksheets("LATURAP").Activate
.........
You could try this(comments added in code)...
Sub Laturap()
Dim ws1 As Worksheet, ws2 As Worksheet, i As Long
Set ws1 = ThisWorkbook.Sheets("LATURAP")
Set ws2 = ThisWorkbook.Sheets("1708")
x = 1
With ws1 'wrap your code in the worksheet variable
For i = 3 To .Cells(.Rows.Count, 1).End(xlUp).Row 'you can assign the last row as a variable and use it, this cuts down the lines of code
If Left(.Range("A" & i), 6) = 170889 Then 'check the first 6 characters in each cell in Col A for the value
With .Range("A" & i).Resize(, 10) 'if a match select the range in the row from Col A to Col J using resize.
.Copy Destination:=ws2.Cells(Rows.Count, 1).End(xlUp).Offset(x, 1) 'copy the range pan paste to the first cell in ColB in ws2
.Clear 'clear the range in ws1
x = x + 1 'increases 1 to paste to the next empty row, must be within the If statement
End With
End If
Next i
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
End Sub
I will try and explain this as best I can.
worksheet 1 has two cells. 1st cell contains a 6 digit code (eg.123456).
Worksheet 2 contains several tabs, each tab will contain the 6 digit code and next to it a figuire.
I would like the 2nd cell in worksheet 1 to find the 6 digit codes in all of the tabs in worksheet 2 and total the figures next to them.
Is this possible with V-lookup?
Are you looking to sum the values in the column if they match the 6 digit code?
If so, sumif might be what you seek.
http://office.microsoft.com/en-us/excel-help/sumif-HP005209292.aspx
sumif for arrays:
http://support.microsoft.com/kb/275165
Update:
Not a complete solution, but you could create a master table with the vba code below and create a pivot table on the summary table and do sum,counts, etc on all the unique 6 digit codes you want to summarize. You'll need to edit the range you're seeking and the range will have to be the same on all the worksheets. The "Summary-Table" worksheet will be created if not already there and be overwritten if it already exits.
Sub Summarize_Table_With_Formulas()
Dim Sh As Worksheet
Dim Newsh As Worksheet
Dim myCell As Range
Dim ColNum As Integer
Dim RwNum As Long
Dim Basebook As Workbook
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
'Delete the sheet "Summary-Table" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Worksheets("Summary-Table").Delete
On Error GoTo 0
Application.DisplayAlerts = True
'Add a worksheet with the name "Summary-Table"
Set Basebook = ThisWorkbook
Set Newsh = Basebook.Worksheets.Add
Newsh.Name = "Summary-Table"
'The links to the first sheet will start in row 2
RwNum = 1
For Each Sh In Basebook.Worksheets
If Sh.Name <> Newsh.Name And Sh.Visible Then
ColNum = 1
RwNum = RwNum + 1
'Copy the sheet name in the A column
Newsh.Cells(RwNum, 1).Value = Sh.Name
For Each myCell In Sh.Range("A1:B2") '<--Change the range here
ColNum = ColNum + 1
Newsh.Cells(RwNum, ColNum).Formula = _
"='" & Sh.Name & "'!" & myCell.Address(False, False)
Next myCell
End If
Next Sh
Newsh.UsedRange.Columns.AutoFit
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
adapted VBA code from http://www.rondebruin.nl/win/s3/win003.htm