I have a macro that does work, it's just really slow when there is a lot of data and I'm hoping that someone on here can help me to speed it up.
When my VBA does is check the columns of a sheet for the value "NULL" and if it's there it clears that cell. Here's the code:
Sub RemoveNullColumn()
Dim c, count, r, lc, FirstCell
Application.ScreenUpdating = False
count = 0
r = ActiveCell.row 'lets you choose where you want to start even if it is not at "A1"
c = ActiveCell.Column 'lets you choose where you want to start even if it is not at "A1"
c = GetLetterFromNumber(c) 'Gets the column letter from the number provided above
FirstCell = c & r 'sets the cell that you selected to start in so that you will end thereafter removing all the NULL
lc = ActiveSheet.Cells(1, Columns.count).End(xlToLeft).Column 'Finding the last used column
For H = ActiveCell.Column To lc Step 1 'Starts with where you selected a cell and moves right to the last column
For x = 1 To Range(c & Rows.count).End(xlUp).row Step 1 'Starts with the first row and moves through the last row
count = count + 1
If Range(c & x).Value = "NULL" Then 'Checks the contents fo the cell to see if it is "NULL"
Range(c & x).Clear
End If
If count = 1000 Then 'This was used testing but is not seen with the ScreenUpdating set to false
Range(c & x).Select
count = 1
End If
Next x
ActiveCell.Offset(0, 1).Select 'select the next column
c = ActiveCell.Column
c = GetLetterFromNumber(c) 'get the letter of the next column
Next H
Application.ScreenUpdating = True
MsgBox "Finished"
Range(FirstCell).Select
End Sub
Function GetLetterFromNumber(Number)
GetLetterFromNumber = Split(Cells(1, Number).Address(True, False), "$")(0)
End Function
When there are not a lot of rows it is pretty fast, but there are a lot of rows it is slow.
I have a file that I ran it on that has columns from A to AD and 61k+ rows, it took more than 30 minutes to finish and I'm hoping to make that much faster.
Instead of looking into Every single cell in the worksheet, use Replace function which is far faster :(you may need to edit it customize it to your needs)
Example :
Sub RemoveNullColumn()
Dim targetSheet As Worksheet
Set targetSheet = ActiveSheet 'TODO: replace with a stronger object reference
targetSheet.Cells.Replace What:="NULL", Replacement:="", LookAt:=xlWhole, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End Sub
This will make sure you will preserve the format.
If you want to clear NULL using ActiveCell as reference:
Range(ActiveCell, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Replace What:="NULL", Replacement:="", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=True, SearchFormat:=False, _
ReplaceFormat:=False
Please give this a try...
Sub RemoveNullColumn()
Dim lr As Long, lc As Long
Dim rng As Range, cell As Range, FirstCell As Range
With Application
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
lr = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lc = Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set FirstCell = ActiveCell
Set rng = Range(Cells(1, FirstCell.Column), Cells(lr, lc))
For Each cell In rng
If cell.Value = "NULL" Then
cell.Clear
End If
Next cell
With Application
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.ScreenUpdating = True
End With
MsgBox "Finished"
End Sub
Use a .Find/.FindNext to collect all of the matching cells into a Union then clear the contents of the Union'ed cells.
Option Explicit
Sub noNULLs()
Dim firstAddress As String, c As Range, rALL As Range
With ActiveSheet.Cells 'This should be named worksheet like Worksheets("sheet1")
Set c = .Find("NULL", MatchCase:=True, _
LookIn:=xlValues, LookAt:=xlWhole)
If Not c Is Nothing Then
Set rALL = c
firstAddress = c.Address
Do
Set rALL = Union(rALL, c)
Set c = .FindNext(c)
Loop While c.Address <> firstAddress
rALL.Clear
End If
End With
End Sub
Related
in a column I have multiple values (Vat numbers) but not in consecutive rows, for example in A1, A5, A8,...
I need to copy each value from column A and paste them in a new column but reordered in consecutive rows, for example B1,B2,B3,...
The following macro does this job but only if in the value it is included the symbol "#" because it uses the function "Array" (if I replace # with numbers as 1,2,3,4,5,6,7,8,9, I get the same VAT number multiple times and I do not require it).
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
MyArr = Array("#")
Set NewSh = Sheets("Sheet2")
With Sheets("Sheet2").Range("A1:A100")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
'If you use LookIn:=xlValues it will also work with a
'formula cell that evaluates to "#"
'Note : I use xlPart in this example and not xlWhole 'MyArr(I)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
NewSh.Range("A" & Rcount).Value = Rng.Value
' Use this if you only want to copy the value
' NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Thanks
Marco
I am facing some issues with VBA. Let me explain what I am trying to achieve. I have 2 sheets in 1 workbook. They are labelled "Sheet1" and "Sheet2."
In "Sheet1," there are 100 rows and 100 columns. In column A, it is filled with eg: SUBJ001 all the way to SUBJ100. In "Sheet2," there is only 1 Column A, with a range of rows. Eg: "SUBJ003, SUBJ033, SUBJ45." What I am trying to achieve is to use my mouse, highlight the column A in "Sheet2," and compare each individual cell with the cells in column A. Should there be a match, it will copy the entire row and paste them in a new sheet that the macro creates in the same workbook. However, i am experiencing an out of range error at Set Rng =.Find(What:=Arr(I), ... Thanks!
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set Rng = Application.InputBox("Select target range with the mouse", Type:=8)
MyArr = Rng
Set NewSh = Worksheets.Add
With Sheets("Sheet1").Range("A:A")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
Rng.EntireRow.Copy NewSh.Range("A" & Rcount)
' Use this if you only want to copy the value
' NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
MyArr = Rng is setting MyArr to be a two-dimensional array where the first rank corresponds to the rows in Rng and the second rank corresponds to the columns in Rng.
Assuming you only have one column in Rng, then your Find statement should refer to the values in that first column using MyArr(I, 1), i.e.
Set Rng = .Find(What:=MyArr(I, 1), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
I have the code below and works fine, but I only want to copy cells with Values. I have blank data in the middle, as I will delete that does not make sense to copy them too.
Sub FindAgain()
'
' FindAgain Macro
'
Dim Ws As Worksheet
Dim LastRow As Long
AC = ActiveCell.Column
Set Ws = Worksheets("Sheet1")
LastRow = Ws.Cells(Rows.Count, "B").End(xlUp).Row
Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveCell.Offset(1, 0).Select
Range(ActiveCell, Cells(LastRow, AC)).Select
End Sub
Any idea how I can better write it? With Loop maybe? Thanks!
I assume that after Range(ActiveCell, Cells(LastRow, AC)).Select you see a region selected that you want to copy ignoring blank cells. One way to go about it is to iterate over all the cells in Selection, check if they are not empty and copy them:
Dim c As Range
Dim i As Long
' store current row for every column separately
Dim arrRowInCol() As Long
ReDim arrRowInCol(Selection.Column To Selection.Column + Selection.Columns.Count - 1)
For i = LBound(arrRowInCol) To UBound(arrRowInCol)
' init the first row for each column
arrRowInCol(i) = Selection.Row
Next i
For Each c In Selection
If Len(Trim(c)) <> 0 Then
c.Copy Destination:=Sheets("Sheet2").Cells(arrRowInCol(c.Column), c.Column)
arrRowInCol(c.Column) = arrRowInCol(c.Column) + 1
End If
Next c
Found a way to do what I want: At least is working, i am newby so, for you guys may seem funny or bad, for me is great =D
Sub FindAgain()
'
' FindAgain Macro
'
Dim Ws As Worksheet
Dim LastRow As Long
Dim c As Range
Dim i As Integer
Dim j As Integer
AC = ActiveCell.Column
Set Ws = Worksheets("Sheet1")
LastRow = Ws.Cells(Rows.Count, "B").End(xlUp).Row
i = 15
j = 7
Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveCell.Offset(1, 0).Select
Range(ActiveCell, Cells(LastRow, AC)).Select
For Each c In Selection
If Len(Trim(c)) <> "" Then
c.Copy Destination:=Sheets("Sheet1").Cells(i, j)
End If
If c = "" Then
i = i
Else
i = i + 1
End If
j = j
Next c
End Sub
I will start with your code, which actually tries to select the ranges. This is what I have built upon it:
Option Explicit
Public Sub FindMe()
Dim my_range As Range
Dim temp_range As Range
Dim l_counter As Long
Dim my_list As Object
Dim l_counter_start As Long
Set my_list = New Collection
l_counter_start = Cells.Find(What:="Scenario", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Row + 1
For l_counter = l_counter_start To Worksheets("sheet1").Cells(Rows.Count, "B").End(xlUp).Row
If Cells(l_counter, 2) <> "" Then my_list.Add (l_counter)
Next l_counter
For l_counter = 1 To my_list.Count
Set temp_range = Range(Cells(my_list(l_counter), 2), Cells(my_list(l_counter), 4))
If my_range Is Nothing Then
Set my_range = temp_range
Else
Set my_range = Union(my_range, temp_range)
End If
Next l_counter
my_range.Select
End Sub
It works upon a scenario like this:
Pretty much it works like this:
We declare two ranges.
The range my_range is the one to be selected at the end.
The range temp_range is only given, if there is a value in the second column.
Then there is a union of both ranges, and my_range is selected at the end of the code.
I am trying to copy all worksheets, one at a time, and pasting into a new worksheet. These files come from multiple third parties so the worksheets can vary. I'm running into a problem below when trying to determine last row Lrow and last column Lcol because an error appears saying Object doesn't support this property or method. I do plan on submitting this to my work so any help with error proofing or general macro tips are appreciated.
Sub ws_copy()
Dim Lrow As Long
Dim Lcol As Long
Dim Pasterow As Long
Dim WSCount As Integer
Dim i As Integer
'On Error Resume Next
'Application.DisplayAlerts = False
i = Application.InputBox(prompt:="Enter the place order of first tab to be copied.", Title:="Worksheet Consolidation", Type:=1)
If IsEmpty(i) = True Then
Exit Sub
Else
If IsNumeric(i) = False Then
MsgBox "Enter a numeric value."
Else
If IsNumeric(i) = True Then
Worksheets.Add(before:=Sheets(1)).Name = "Upload"
WSCount = Worksheets.Count
For i = i + 1 To WSCount
Lrow = Worksheets(i).Find("*", After:=Cells(1, 1), _
LookIn:=xlFormulas, _
Lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Lcol = Worksheets(i).Find("*", After:=Cells(1, 1), _
LookIn:=xlFormulas, _
Lookat:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Pasterow = Lrow + 1
Workbook.Worksheets(i).Range(Cells(1, 1), Cells(Lrow, Lcol)).Copy
Workbook.Worksheets("Upload").Cells(Pasterow, 1).Paste
Next i
Else
Exit Sub
End If
End If
End If
'On Error GoTo 0
'Application.DisplayAlerts = False
End Sub
A common way to find the last row/column is:
With Worksheets(i)
Lrow = .Cells(.Rows.Count, 1).End(xlUp).Row
Lcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
hth
Based on the comment that:
I can't assume any one column or row has the last piece of data because of the variety of the files received.
You should look at using the UsedRange property of the Worksheet (MSDN). UsedRange expands as more data is entered onto the worksheet.
Some people will avoid using UsedRange because if some data has been entered, and then deleted then UsedRange will include these 'empty' cells. The UsedRange will update itself when the workbook is saved. However, in your case, it doesn't sound like this is a relevant issue.
An example would be:
Sub Test()
Dim wsSource As Worksheet
Dim wsTarget As Worksheet
Dim rngSource As Range
Dim rngTarget As Range
Set wsSource = ThisWorkbook.Worksheets("Sheet1")
Set wsTarget = ThisWorkbook.Worksheets("Sheet2")
Set rngSource = wsSource.UsedRange
rngSource.Copy Destination:=wsTarget.Cells
End Sub
Here is a method of finding the last used row and last used column in a worksheet. It avoids the issues with UsedRange and also your issues of not knowing which row might have the last column (and which column might have the last row). Adapt to your purposes:
Option Explicit
Sub LastRowCol()
Dim LastRow As Long, LastCol As Long
With Worksheets("sheet1") 'or any sheet
If Application.WorksheetFunction.CountA(.Cells) > 0 Then
LastRow = .Cells.Find(what:="*", after:=[A1], _
LookIn:=xlFormulas, _
searchorder:=xlByRows, _
searchdirection:=xlPrevious).Row
LastCol = .Cells.Find(what:="*", after:=[A1], _
LookIn:=xlFormulas, _
searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
Debug.Print LastRow, LastCol
End Sub
Although the basic technique has been long used, Siddarth Rout, some time ago, posted a version adding COUNTA to account for the case where the worksheet might be empty -- a useful addition.
If you want to merge data on each sheet into one MasterSheet, run the script below.
Sub CopyRangeFromMultiWorksheets()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'Delete the sheet "RDBMergeSheet" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("RDBMergeSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
'Add a worksheet with the name "RDBMergeSheet"
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "RDBMergeSheet"
'loop through all worksheets and copy the data to the DestSh
For Each sh In ActiveWorkbook.Worksheets
If sh.Name <> DestSh.Name Then
'Find the last row with data on the DestSh
Last = LastRow(DestSh)
'Fill in the range that you want to copy
Set CopyRng = sh.Range("A1:G1")
'Test if there enough rows in the DestSh to copy all the data
If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
MsgBox "There are not enough rows in the Destsh"
GoTo ExitTheSub
End If
'This example copies values/formats, if you only want to copy the
'values or want to copy everything look at the example below this macro
CopyRng.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
'Optional: This will copy the sheet name in the H column
DestSh.Cells(Last + 1, "H").Resize(CopyRng.Rows.Count).Value = sh.Name
End If
Next
ExitTheSub:
Application.Goto DestSh.Cells(1)
'AutoFit the column width in the DestSh sheet
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Also, see the link below for some other options to do this slightly differently.
http://www.rondebruin.nl/win/s3/win002.htm
I have for the most part it working. I can't seem to get through the CopyRng block to set it for each sheet and gather the entire row where the cells are color filled. Set CopyRng = sh.Cells().Interior.Color = vbOrange sh.Cells().EntireRowCan anyone help?
Module1:
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
End Function
Module2:
Option Explicit
Sub CopyRangeFromMultiWorksheets()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range
Dim tbl As ListObject
Dim Cell As Range
Dim clrOrange As Long
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
' Delete the summary sheet if it exists.
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Worksheets("SummarySheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
' Add a new summary worksheet.
Set DestSh = ThisWorkbook.Worksheets.Add
DestSh.Name = "SummarySheet"
Range("A1").FormulaR1C1 = "=TODAY()"
Range("A3:G3").Font.Bold = True
Range("A3") = "Vendor"
Range("B3") = "Account#"
Range("C3") = "Job/Dept"
Range("D3") = "Cost Code/Account"
Range("E3") = "PO"
Range("F3") = "Bill Date"
Range("G3") = "Bill Date2"
clrOrange = RGB(255, 192, 0)
' Loop through all worksheets and copy the data to the
' summary worksheet.
For Each sh In ThisWorkbook.Worksheets
For Each tbl In sh.ListObjects
For Each Cell In tbl.DataBodyRange
If sh.Name <> DestSh.Name Then
' Find the last row with data on the summary worksheet.
Last = LastRow(DestSh)
' Specify the range to place the data. Select entire row where cells are orange.
If Cell.Interior.Color = clrOrange Then
If CopyRng Is Nothing Then
Set CopyRng = Cell
Else
Set CopyRng = Union(CopyRng, Cell)
End If
End If
' This statement copies values and formats from each
' worksheet.
Cell.EntireRow.Copy
With DestSh.Cells(Last + 1, "A")
.PasteSpecial xlPasteValues
Application.CutCopyMode = False
End With
End If
Next
Next
Next
ExitTheSub:
Application.GoTo DestSh.Cells(1)
' AutoFit the column width in the summary sheet.
DestSh.Columns.AutoFit
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
You need to loop through the cells and check each to see if they are orange, then add them to CopyRng one by one:
Dim Cell as Range
For Each Cell in sh.Range("A1:A50") 'Or whatever the range is where orange cells can be
If Cell.Interior.Color = vbOrange Then
If CopyRng is Nothing then
Set CopyRng = Cell
Else
Set CopyRng = Union(CopyRng, Cell)
End If
EndIf
Next
CopyRng.Copy
etc.