Excel VBA - Loop to check entire column range rather than each cell - vba

I have written a script which checks a range of cells in column range 4 (Column D) for non-blank values, if it finds a non blank value, it copies that value and pastes it to a cell in column range 6 (Column F). The script runs, but it is awfully slow, the script takes 5 minutes to process and complete its run. Is there any way to improve this script so that it can pre-check the range before it copies and pastes the values across? It seems that the copy / paste function is slowing it down.
Code below
Sub ArrayCopyPaste()
Dim J as Integer
Application.Calculation = xlCalculationManual
For J = 2 To 500
If Cells(J, 4).Value <> "" Then
Cells(J, 4).Copy
Cells(J, 6).PasteSpecial Paste:=xlPasteValues
End If
Next J
Application.Calculation = xlCalculationAutomatic
End Sub

Here's one way:
Sub test()
Dim r1, r2, n As Long
With Sheets("Sheet1") '~~> change to suit
Dim lrow As Long
lrow = .Range("D" & .Rows.Count).End(xlUp).Row
r1 = Application.Transpose(.Range("D2:D" & lrow))
r2 = Application.Transpose(.Range("F2:F" & lrow))
For n = LBound(r1) To UBound(r1)
If r1(n) <> "" Then r2(n) = r1(n)
Next
.Range("F2:F" & lrow) = Application.Transpose(r2)
End With
End Sub
Transfer range data to array, then do the comparison process array to array.
Then return the array to range. HTH.
Important: Application.Transpose have limitation. I can handle only a few thousand data.
Follow up: Try this for deleting
Dim rngToDelete As Range, k As Long
With Sheets("Sheet1") '~~> change to suit
For k = 2 To 500
If .Cells(k, 6).Value = "" Then
If rngToDelete Is Nothing Then
Set rngToDelete = .Cells(k, 6)
Else
Set rngToDelete = Union(rngToDelete, .Cells(k, 6))
End If
End If
Next
rngToDelete.Delete xlUp
'rngToDelete.EntireRow.Delete xlUp ~~> use this if you want to delete entire row.
End With
Determine all the target range first then delete in one go. HTH.

Try simply doing this first and see if it makes a difference:
Dim currentCalculation As Variant
currentCalculation = Application.Calculation
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
For J = 2 To 500
If Cells(J, 4).Value <> "" Then
Cells(J, 4).Copy
Cells(J, 6).PasteSpecial Paste:=xlPasteValues
End If
Next J
Application.ScreenUpdating = True
Application.Calculation = currentCalculation
Another thought. Have you tried just doing this?
For J = 2 To 500
If Cells(J, 4).Value <> "" Then
Cells(J, 6).Value = Cells(J, 4).Value
End If
Next J

It won't make any difference to your target column if the blanks are copied, so don't bother checking for them. Don't loop -- just copy the entire column.
Sub CopyColumn()
' copying this way does not use your clipboard
Columns("D").Copy Columns("F")
End Sub
If you only need a portion of the column, specify the range to copy rather than the entire column:
Sub CopyPartOfColumn()
' copying this way does not use your clipboard
Range("D2:D500").Copy Range("F2:F500")
End Sub
You mention in a comment below your question that you want the resulting column to be a consolidated list of the values without the blanks. You can quickly do that by deleting the blanks from the column or range, once again without looping. Run this after you've copied the values you want.
Sub RemoveBlanks()
Range("F2:F500").SpecialCells(xlCellTypeBlanks).Delete Shift:=xlUp
End Sub

Related

VBA to Move raw from source sheet to different sheets based on different columns

Dears, Am new in VBA, here below i create this vba macro for my work needs, to distribute data rows from Sheet1 to different sheets (2,3,4,5) & list sheet (6) based on:
if cell value in sheet1 column A match with sheet6 column A then move raw from sheet1 to sheet2
if cell value in sheet1 column A match with sheet6 column B then move raw from sheet1 to sheet3
and so on.
but my code took long time (very slow)
need your help please.
Sub distribute()
Application.ScreenUpdating = False
Dim Base As Worksheet
Dim List As Worksheet
Dim i As Integer
Dim LastRow As Long
Set Base = Sheets(1)
Set List = Sheets(7)
LastRow = Base.Cells(Base.Rows.Count, "B").End(xlUp).Row
For i = LastRow To 1 Step -1
For b = 2 To LastRow
If Base.Cells(i, 2).Value = List.Cells(b, 1).Value Then
Base.Rows(i).EntireRow.Copy _
Destination:=Sheets(2).Rows(Sheets(2).Cells(Sheets(2).Rows.Count, 1).End(xlUp).Row + 1).EntireRow
Base.Rows(i).EntireRow.Delete
Else
If Base.Cells(i, 2).Value = List.Cells(b, 2).Value Then
Base.Rows(i).EntireRow.Copy _
Destination:=Sheets(3).Rows(Sheets(3).Cells(Sheets(3).Rows.Count, 1).End(xlUp).Row + 1).EntireRow
Base.Rows(i).EntireRow.Delete
Else
If Base.Cells(i, 2).Value = List.Cells(b, 3).Value Then
Base.Rows(i).EntireRow.Copy _
Destination:=Sheets(4).Rows(Sheets(4).Cells(Sheets(4).Rows.Count, 1).End(xlUp).Row + 1).EntireRow
Base.Rows(i).EntireRow.Delete
Else
If Base.Cells(i, 2).Value = List.Cells(b, 4).Value Then
Base.Rows(i).EntireRow.Copy _
Destination:=Sheets(5).Rows(Sheets(5).Cells(Sheets(5).Rows.Count, 1).End(xlUp).Row + 1).EntireRow
Base.Rows(i).EntireRow.Delete
End If
End If
End If
End If
Next b
Next i
Application.ScreenUpdating = True
End Sub
I run your code using some sample data sheet, for me it runs fast enough. You may add these ff code to optimize you procedure.
Sub distribute()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
.....rest of the code
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
End Sub
Another tip if you want. Try to use CUT instead of copy then you can delete the code:
Base.Rows(i).EntireRow.Delete
Use the cut instead of copy
If Base.Cells(i, 2).Value = List.Cells(b, 2).Value Then
Base.Rows(i).EntireRow.Cut Destination:=Sheets(3).Rows(Sheets(3).Cells(Sheets(3).Rows.Count, 1).End(xlUp).Row + 1).EntireRow

Copy / Paste data based on values in adjacent column

Hi I am new to VBA and have hit a wall. Tried piecing together snippets of code with the little I understand but think I am over my head. I would greatly appreciate any help constructing a block of code to achieve the following goal:
In the following worksheet
I am trying to loop through column A and identify any blank cells.
If the cells are blank I would like to copy the values in the range of 4 cells adjacent to the right of the blank cell in column A. For example: if loop identified A2 as blank cell then the loop would copy the values in range("B2:E2")
From here I would like to paste the values below the copied range to only the rows that are not blank in column A. For example: The loop would identify not blank rows in column A as ("A3:A9") and paste data below the copied range to range ("B3:E9")
The loop would stop at the next blank row in column and restart the process
Here is a screen shot of the data:
Here is what I have so far, sorry its not much Thanks in advance!
Sub select_blank()
For Each Cell In Range(ActiveCell, ActiveCell.End(xlDown))
If IsEmpty(ActiveCell.Value) = True Then
ActiveCell.Offset(, 1).Resize(, 5).copy
End If
Next
End Sub
Your code only needs a few tweaks (plus the PasteSpecial!) to get it to work:
Sub select_blank()
Dim cel As Range
With ActiveSheet
'specify that the range to be processed is from row 2 to the
'last used cell in column A
For Each cel In .Range(.Cells(2, "A"), .Cells(.Rows.Count, "A").End(xlUp))
If IsEmpty(cel.Value) Then
'If the cell is empty, copy columns B:F
cel.Offset(, 1).Resize(, 5).Copy
Else
'If the cell is not empty, paste the values previously copied
'NOTE: This relies on cell A2 being empty!!
cel.Offset(, 1).PasteSpecial
End If
Next
End With
Application.CutCopyMode = False
End Sub
I cannot make much sense of what you want, it seems to contradict itself. But, since I highly doubt anyone else is going to help you with this (per the rules), I'll give you a much better start.
Sub Test()
Dim nRow As Integer
nRow = 1
Do Until Range("A" & nRow) = "" And Range("A" & nRow + 1) = ""
If Range("A" & nRow) = "" Then
' do stuff here in the loop
End If
nRow = nRow + 1
Loop
End Sub
Sub copyRange()
Dim rngDB As Range, vDB, rng As Range
Set rngDB = Range("a2", Range("a" & Rows.Count).End(xlUp))
For Each rng In rngDB
If rng = "" Then
vDB = rng.Offset(, 1).Resize(1, 4)
Else
rng.Offset(, 1).Resize(1, 4) = vDB
End If
Next rng
End Sub

Delete row if the column contains text

I known, this question has been asked thousands of times. But every time I picked up a solution appears error when i debug. (error 1004)
I work with a database with about 300000 lines, where more than half do not care. (I know that have filter, but wanted to erase to reduce the file and speed up the process).
Then if the column M has a keyword like "water", "beer" or "vodka" it will delete the row. I mean, don't need to be the exact word, just the keyword.
OBS: Row 1 it's a table title with the frozen line.
Thanks!
The following code works less than 4 seconds for processing your sample data on my machine:
Sub QuickDeleteRows()
Dim Sheet_Data As Worksheet, NewSheet_Data As Worksheet, Data As Range
Dim Sheet_Name As String, Text As String, Water As Long, Beer As Long, Vodka As Long
On Error GoTo Error_Handler
SpeedUp True
Set Sheet_Data = Sheets("SOVI")
Sheet_Name = Sheet_Data.Name
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
ReDim Output(1 To LastRow - 1, 1 To 1) As Long
For i = 1 To LastRow - 1
Text = Cells(i + 1, 13)
Water = InStr(Text, "water")
Beer = InStr(Text, "beer")
Vodka = InStr(Text, "vodka")
If Water > 0 Or Beer > 0 Or Vodka > 0 Then Output(i, 1) = 1
Next
[S2].Resize(LastRow - 1, 1) = Output
LastColumn = Cells(2, Columns.Count).End(xlToLeft).Column
Set Data = Sheet_Data.Range(Cells(1, 1), Cells(LastRow, LastColumn))
Set NewSheet_Data = Sheets.Add(After:=Sheet_Data)
Data.AutoFilter Field:=19, Criteria1:="=1"
Data.Copy
With NewSheet_Data.Cells
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteAll
.Cells(1, 1).Select
.Cells(1, 1).Copy
End With
Sheet_Data.Delete
NewSheet_Data.Name = Sheet_Name
NewSheet_Data.Columns(19).Clear
Safe_Exit:
SpeedUp False
Exit Sub
Error_Handler:
Resume Safe_Exit
End Sub
Sub SpeedUp(SpeedUpOn As Boolean)
With Application
If SpeedUpOn Then
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
.DisplayStatusBar = False
.DisplayAlerts = False
Else
.ScreenUpdating = True
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.DisplayStatusBar = True
.DisplayAlerts = True
End If
End With
End Sub
In the future, please post code you've tried first for the community to help you out. That being said, try this out:
Sub Test()
Dim x as Long
Dim i as Long
x = Sheets("SOVI").Range("M" & Rows.Count).End(xlUp).Row
For i = x to 2 Step -1
If InStr(1, Range("M" & i).Value, "water", vbTextCompare) Or InStr(1, Range("M" & i).Value, "beer", vbTextCompare) Or InStr(1, Range("M" & i).Value, "vodka", vbTextCompare) Then
Range("M" & i).entirerow.delete
End If
Next i
End Sub
I would use a slightly different approach, with the Like and with Select Case - this will give you more versatility in the future if you would want to expand it to more types of drinks.
Sub FindDrink()
Dim lRow As Long
Dim i As Long
Dim sht As Worksheet
' always set your sht, modify to your sheet name
Set sht = ThisWorkbook.Sheets("Sheet1")
lRow = sht.Cells(sht.Rows.Count, "M").End(xlUp).Row
For i = lRow To 2 Step -1
Select Case True
Case (sht.Cells(i, "M").Value Like "*beer*") Or (sht.Cells(i, "M").Value Like "*water*") Or (sht.Cells(i, "M").Value Like "*vodka*")
Range("M" & i).EntireRow.Delete
Case Else
' if you decide to do other things in the future for other values
End Select
Next i
End Sub
use excel built in filtering functions for the maximum speed
Autofilter
Option Explicit
Sub main()
Dim keysToErase As Variant, key As Variant
keysToErase = Array("water", "beer", "vodka") '<--| list your keywords to delete matching column "M" rows with
Application.DisplayAlerts = False '<--| prevent alerts dialog box from appearing at every rows deletion
With Workbooks("test").Worksheets("SOVI").Range("A1").CurrentRegion '<--| this gets the range of all contiguous cells to "A1"
For Each key In keysToErase '<--| loop through keys
.AutoFilter field:=13, Criteria1:="*" & key & "*" '<--| filter column "M" with key
If Application.WorksheetFunction.Subtotal(103, .Cells.Resize(, 1)) > 1 Then .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Delete '<--| delete filtered cells, if any
Next key
.Parent.ShowAllData '<--| .. show all rows back...
End With
Application.DisplayAlerts = True '<--| allow alerts dialog box back
End Sub
AdvancedFilter
Option Explicit
Sub main2()
Application.DisplayAlerts = False '<--| prevent alerts dialog box from appearing at every rows deletion
With Workbooks("test").Worksheets("SOVI").Range("A1").CurrentRegion '<--| this gets the range of all contiguous cells to "A1"
.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=.Parent.Range("U1:U4") '<--| this filters on all keys you placed in cells "U2:U4" with cell "U1" with wanted data header
If Application.WorksheetFunction.Subtotal(103, .Cells.Resize(, 1)) > 1 Then .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Delete '<--| delete filtered cells, if any
.Parent.ShowAllData '<--| .. show all rows back...
End With
Application.DisplayAlerts = True '<--| allow alerts dialog box back
End Sub
Try with Below code
Sub test()
Application.DisplayAlerts = False
Dim lastrow As Long
Dim i As Long
Dim currentrng As Range
lastrow = Range("M" & Rows.Count).End(xlUp).Row
For i = lastrow To 2 Step -1
Set currentrng = Range("M" & i)
If ((currentrng Like "*water*") Or (currentrng Like "*beer*") Or (currentrng Like "*vodka*")) Then
currentrng.EntireRow.Delete shift:=xlUp
End If
Next i
Application.DisplayAlerts = True
End Sub

Copy and paste a range of cells from one sheet to another then clear data from orignal cells

I have the below code that works well, however what I will like to do is have the code modified to copy the data it will clear to Sheet2 for further investigating the continue to clear from the original sheet. All the code itself does is look at G and H. If H is smaller than G it then clears the contents of A:J. What I want now is to still clear the contents if the criteria is met however I want a copy of the cells copied to Sheet2 as well.
Sub ClearRange()
Dim myLastRow As Long
Dim i As Long
Application.ScreenUpdating = False
' Find last row
myLastRow = Cells(Rows.Count, "G").End(xlUp).Row
' Loop through range
For i = 5 To myLastRow
If Cells(i, "H").Value < Cells(i, "G").Value Then Range(Cells(i, "A"), Cells(i, "J")).ClearContents
Next i
Application.ScreenUpdating = True
End Sub
Thanks in advance for any assistance you can provide.
You can just update this portion of your code:
' Loop through range
For i = 5 To myLastRow
If Cells(i, "H").Value < Cells(i, "G").Value Then
With Range(Cells(i, "A"), Cells(i, "J"))
.Copy
Sheets("Sheet2").Paste Destination:=Sheets("Sheet2").Range("A" & i)
.ClearContents
End With
End If
Next

How can I traverse a column and copy its values to two more columns?

I have been trying to create a macro that will go through a spreadsheet and copy figures in Cells E, then paste them into Cell K and L, then repeat as the macro transverse column E. i.e. E1 will be copied to K1 and L1, E2 will be copied to K2, L2 etc...
This is what i have done so far:
Sub uy()
'
' Macro1 Macro
' lo
'
Range("E299").Select
Do Until IsEmpty(ActiveCell)
If ActiveCell.Value < 0 Then
Selection.Copy
Range("K299").Select
Do Until IsEmpty(ActiveCell)
ActiveSheet.Paste
Loop
Range("L299").Select
Do Until IsEmpty(ActiveCell)
ActiveSheet.Paste
Loop
Else
Range("L299").Select
Do Until IsEmpty(ActiveCell)
ActiveSheet.Paste
Loop
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
When i run the macro it just highlights cell E229 with a broken line box and Cells K299, L299 are left blank. I feel Range("K299").Select, Do Until IsEmpty(ActiveCell), ActiveSheet.Paste part is selecting and copying a blank cell, so it will terminate itself as it meets the "Do Until IsEmpty(ActiveCell)" criteria.
Is there a way for me to fix this?
I'm not quite sure if i got you right.
but if you want to just copy one range to an other then
this would do it.
Private Sub CommandButton1_Click()
For i = 1 To Cells(Rows.Count, 5).End(xlUp).Row
If Cells(i, 5) <> "" Then
Cells(i, 11).Value = Cells(i, 5).Value
Cells(i, 12).Value = Cells(i, 5).Value
End If
Next i
End Sub
This marco works as long as cell 'i' in column E isn't empty
and takes the value of cell 'i' in column E and puts it into column K and L
Kind regards
First, don't use Activate or Select. They're 99% useless in most code. Next, don't use copy and paste. It's slow for this kind of approach.
The following code is much more streamlined and faster.
Sub EtoKL()
Dim WS As Worksheet
Dim LRow As Long, Iter As Long
Set WS = ThisWorkbook.Sheets("Sheet1") 'Change as necessary.
With WS
LRow = .Range("E" & .Rows.Count).End(xlUp).Row 'Get last used row in Column E.
For Iter = 1 To LRow 'Iterate from 1 to last used row.
Union(.Range("K" & Iter), .Range("L" & Iter)).Value = .Range("E" & Iter).Value
Next
End With
End Sub
Let us know if this helps.
I think something like this would work.
Sub Copy()
Dim intRowCount as Long
Dim intLastRow as Long
intRowCount = 2
intLastRow = Application.CountA(Sheets(1).Range("e:e"))
For intRowCount = 2 To intLastRow
Sheets(1).Range("K" & intRowCount ).Value = Sheets(1).Range("E" & intRowCount ).Value
Sheets(1).Range("L" & intRowCount ).Value = Sheets(1).Range("E" & intRowCount ).Value
Next
End Sub