Deleting rows based on criteria - vba

I have a little code so I can move specific rows to a specific sheet which is structured as follows:
sheet 1 (contains all data)
sheet 2 (the destination sheet of rows to move)
So basically the code looks for a keyword on a specific column, and copies all rows that meet that criteria on the specified column from sheet 1 to sheet 2, it does that like a charm. The problem I have is because of data organization, I need to delete the rows once they have been copied, I tried using the .cut target instead of .copy target, and it works too, but it takes extremely long (about 1+ min), and it looks like that whole time is frozen as it doesn't let you select anything.
Any suggestions to accomplish this more efficiently? I am learning VBA, so please bear with me.
Sub Copydatatoothersheet()
Dim c As Range
Dim j As Integer
Dim Source As Worksheet
Dim Target As Worksheet
' Change worksheet designations as needed
Set Source = ActiveWorkbook.Worksheets("Sheet1")
Set Target = ActiveWorkbook.Worksheets("Sheet2")
j = 3 ' Start copying to row 3 in target sheet
Application.ScreenUpdating = False
For Each c In Source.Range("BB:BB")
If c = "UNPAID" Then
'THIS IS THE LINE WHERE I REPLACE COPY WITH CUT
Source.Rows(c.Row).Copy Target.Rows(j)
j = j + 1
End If
Next c
Application.ScreenUpdating = True
End Sub

Try store the desired ranges in a variable then delete the entire rows of that stored range
Sub Copydatatoothersheet()
Dim c As Range
Dim j As Integer
Dim source As Worksheet
Dim target As Worksheet
Dim oRange As Range
' Change worksheet designations as needed
Set source = ActiveWorkbook.Worksheets("Sheet1")
Set target = ActiveWorkbook.Worksheets("Sheet2")
j = 3 ' Start copying to row 3 in target sheet
Application.ScreenUpdating = False
For Each c In source.Range("BB:BB")
If c = "UNPAID" Then
'THIS IS THE LINE WHERE I REPLACE COPY WITH CUT
source.Rows(c.Row).Copy target.Rows(j)
If oRange Is Nothing Then Set oRange = c Else Set oRange =
Union(oRange, c)
j = j + 1
End If
Next c
If Not oRange Is Nothing Then oRange.EntireRow.Delete
Application.ScreenUpdating = True
End Sub

Use AutoFilter
Sub foo()
Dim Source As Worksheet
Dim Target As Worksheet
' Change worksheet designations as needed
Set Source = ActiveWorkbook.Worksheets("Sheet1")
Set Target = ActiveWorkbook.Worksheets("Sheet2")
With Source
With .Range("BB:BB" & .Cells(.Rows.Count, "BB").End(xlUp).Row) 'reference its column BB cells from row 1 (header) down to last not empty one
.AutoFilter field:=1, Criteria1:= "UNPAID"' filter referenced cells on 1st column with "UNPAID" content
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 1 Then
With .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
Intersect(.EntireRow, .Parent.UsedRange), .Parent.UsedRange).Copy Destination:=Target.Range("A1") ' if any filtered cell other than the header then copy their entire rows and paste to 'Target' sheet starting from its cell A1
.EntireRow.Delete ‘finally, delete these rows
End With
End If
End With
.AutoFilterMode = False
End With
End Sub
You may also add the ScreenUpdating toggling

Related

VBA Excel - Putting columns into range in right order

so recently I have been looking into using defined ranges to copy data instead of selecting, copying and pasting cells. This way I hope to optimise the performance and the runtime of my code.
Unfortunately I have come to face a problem I wasn't able to solve on my own.
When defining a range I want to rearrange the columns in a different order.
For example:
Set my_range = Sheets("Sheet1").Range("A2:E2,G2:H2,J2:K2,M2")
Works well, as the columns I fill into the range are behind each other in the sheet. But now I have this:
Set yo_range = Sheets("Sheet2").Range("D2,AV2,L2,H2,Q2,AE2,AG2")
If I fill these ranges into a new sheet the yo_range will fill the columns I put into it but not in the order I written down. It will put it down in the order according to the original one. In this example yo_range would put the data in this order into the new sheet:
D2 | H2 | L2 | Q2 | AE2 | AG2 | AV2
How can I fix this? I want the order to be another one than the original one.
Also - as you can see my_range has more columns than yo_range. How can I let yo_range be filled into the new sheet but at certain points leave columns out? For example:
my_range(A2:E2) goes into A2:E2 in the new sheet
yo_range(D2,AV2) goes into A:B in the new sheet, then leave C out and then paste yo_range(L2,H2) into D:E in the new sheet
I hope that I was able to explain my problem well and that there is somebody able and willing to help me. Any help is appreciated.
Edit:
Here's the code that puts the values from the ranges into the new sheet
Do
If Application.WorksheetFunction.CountA(my_range) > 0 Then
my_range.Copy ws.Range("A" & ws.Rows.Count).End(xlUp).Offset(1, 0)
Set my_range = my_range.Offset(1, 0)
Else
Exit Do
End If
Loop
Do
If Application.WorksheetFunction.CountA(yo_range) > 0 Then
yo_range.Copy ws.Range("A" & ws.Rows.Count).End(xlUp).Offset(1, 0)
Set yo_range = yo_range.Offset(1, 0)
Else
Exit Do
End If
Loop
We can see that the Copy method will re-arrange the data left-to-right. Try this:
Option Explicit
Public Sub CheckClipboard()
Dim ws As Worksheet
Dim rngToCopy As Range
Dim objData As Object
Dim varContents As Variant
' test data b,c,d,e,f,g in Sheet1!B1:G1
Set ws = ThisWorkbook.Worksheets("Sheet1")
ws.Range("B1:G1").Value = Array("b", "c", "d", "e", "f", "g")
Set rngToCopy = ws.Range("E1:F1,G1,B1:C1") '<-- note not left-to-right order
rngToCopy.Copy '<-- copy
' this is a late bound MSForms.DataObject
Set objData = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
' copy current cell formula to clipboard
objData.GetFromClipboard
varContents = objData.GetText
Debug.Print varContents '<-- re-arranged left-to-right
' cancel copy
Application.CutCopyMode = False
End Sub
I get this in the immediate window:
b c d e f g
So, using Copy is not going to work for what you want to do.
In order to 'paste' the data in the order that you set it in the Range, you need to iterate each Area of the Range and then each cell (i.e. Range) in each Area. See the test code below which replicates your issue and presents a solution:
Option Explicit
Sub MixColumns()
Dim ws As Worksheet
Dim rngIn As Range
Dim rngOut As Range
Dim lng As Long
Dim rngArea As Range
Dim rngCell As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
' example 1
Set rngIn = ws.Range("B1:C1,E1:F1,G1") '<-- 5 cells, non-contiguous, forward order
Set rngOut = ws.Range("B2:F2") '<-- 5 contiguous cells
rngIn.Copy rngOut '<-- works
' example 2 - OP problem
Set rngIn = ws.Range("E1:F1,G1,B1:C1") '<-- 5 cells, non-contiguous, odd order
Set rngOut = ws.Range("B3:F3") '<-- 5 contiguous cells
rngIn.Copy rngOut '<-- should be e,f,g,b,c but gets b,c,e,f,g
' example 3 - solution for OP problem
Set rngIn = ws.Range("E1:F1,G1,B1:C1") '<-- 5 cells, non-contiguous, odd order
Set rngOut = ws.Range("B4:F4") '<-- 5 contiguous cells
lng = 1 '<-- rngOut cell counter
' iterate areas
For Each rngArea In rngIn.Areas
' iterate cells in area
For Each rngCell In rngArea.Cells
rngOut.Cells(1, lng).Value = rngCell.Value '<-- copy single value
lng = lng + 1 '<-- increment rngOut counter
Next rngCell
Next rngArea '<-- results in e,f,g,b,c
End Sub
Give this output:

Excel VBA - Find matching column headings and delete the column

Apologies if this has been answered before, I'm unable to find anything that matches my specific case.
I have a workbook with 18 sheets, and a variable number of columns per sheet starting at B2. Occasionally the program that generates the sheet will create duplicate columns, due to this, I need a macro triggered by button to search each sheet for matching column headers and then delete one of these columns (the whole column, not just the header).
So far I'm pretty stuck, I've been able to delete all matches from any cell in the sheet, which pretty much wipes the entire sheet out. I just need to match headers and then delete the entire column based on that.
Let me know if you need any more information, and thank you for the help!
What I have so far, the code is doing some other stuff too so this needs to continue working.
Sub RemoveExtras()
Dim MyRange As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
BadCharacters = Array(Chr(10), Chr(13))
wsNumber = Sheets.Count
For Each ws In Worksheets
With ws
For Each MyRange In .UsedRange
If 0 < InStr(MyRange, Chr(10)) Then
For Each i In BadCharacters
MyRange = Replace(MyRange, i, vbNullString)
Next i
End If
For t = 1 To wsNumber
Columns(t).RemoveDuplicates Columns:=Array(1), Header:=xlYes
Next t
Next MyRange
End With
Next ws
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Dictionaries are perfect for handling unique values:
Sub RemoveExtras()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim c As Integer, i As Integer, ws As Worksheet
Dim dict As Object
For Each ws In Worksheets
Set dict = CreateObject("Scripting.Dictionary")
'Find Last column
c = ws.UsedRange.Columns.Count
'Loop backwards
For i = c To 2 Step -1
'If column does not exist in dictionary, then add it
If Not dict.Exists(ws.Cells(2, i).Value) Then
dict.Add ws.Cells(2, i).Value, 1
Else
'Otherwise delete column
ws.Columns(i).Delete Shift:=xlToLeft
End If
Next i
Set dict = Nothing
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Here you do not compare every pair of column headers in the sheet. Also this compares headers across all the worksheets, not just duplicates inside one individual sheet.
See if this helps you
Sub test()
Dim book As Workbook, sheet As Worksheet, text As String
For Each sheet In Worksheets
Set MR = Range("B2:Z2") 'change Z2 as per your requirement
For Each cell In MR
Set BR = Range("B2:Z2") 'change Z2 as per your requirement
For Each cell2 In BR
If cell.Value = cell2.Value Then cell.EntireColumn.Delete
Next
Next
Next sheet
End Sub

VBA filter table and copy

I have a 3 column table in excel named "RFQ_selector". The 2nd column contains yes/no.
I need a macro that will filter the table for only rows that contain 'Yes' in the 2nd column.
Then the macro should copy every cell to the left of a row which contains a yes into a new location on the same sheet. Pasting them in a list starting at cell F25
I'm getting stuck, can someone help please.
Thanks
Sub CopyYes()
Dim c As Range
Dim j As Integer
Dim Source As Worksheet
Dim Target As Worksheet
' Change worksheet designations as needed
Set Source = ActiveWorkbook.Worksheets("Trader")
Set Target = ActiveWorkbook.Worksheets("Sheet2")
j = 1 ' Start copying to row 1 in target sheet
For Each c In Source.Range("C8:C22") ' Do 30 rows
If c = "yes" Then
Source.Rows(c.Row).Copy Target.Rows(j)
j = j + 1
End If
Next c
End Sub
I've modified your sub to reflect your desired changes:
Copy every cell to the left of a row which contains a yes into a new location on the same sheet. Pasting them in a list starting
at cell F25
It does not filter, there was no filtering happening in your provided code but the output only includes information for the "Yes" columns
Sub CopyYes()
Dim c As Range
Dim j As Integer
Dim Source As Worksheet
'Target worksheet not needed, pasting to source worksheet
' Change worksheet designations as needed
Set Source = ActiveWorkbook.Worksheets("Sheet1")
j = 25 'Start copying to F25
For Each c In Source.Range("B2:B30") 'Change the range here to fit the range in which your data for Yes/No is stored
If c = "Yes" Then 'Verify capitalization here, difference between "Yes" and "yes"
c.Offset(0, -1).Copy Source.Range("F" & j) 'Copy the cell to the left of the Yes/No column and paste on same sheet starting at row F25
j = j + 1
End If
Next c
End Sub

How do I loop through two columns and select rows and add to that selection of rows?

I'm fairly new to VBA. I'm currently trying to find a faster way to copy and paste information by using Macros. I'm not sure how to code this.
I have two columns I want to use with a For Each loop.
I wanted to loop through each row of these two columns and use an If function. If the first row has a value in Column B (Column B cell <> "" Or Column B cell <> 0) then, select that row (i.e. Range("A1:B1")).
After the loop, I will copy whatever is selected and paste it to a specific row.
However, I want to keep adding to that selection as it loops through each row and only if it satisfies the If condition, so I'm able to copy it all once at the end. How do I go about combining this?
A B
1 Abc 1
2 Def 2
3 Geh 3
This is how you can expand current selection:
Sub macro1()
Set selectedCells = Cells(1, 2)
Set selectedCells = Application.Union(selectedCells, Cells(2, 3))
selectedCells.Select
End Sub
I'm sure you can manage the rest of your code by yourself, it's really easy. You already mentioned everything you need: For Each cell In Range("B1:B5") and If statement
Please try the below code
Sub test()
Application.ScreenUpdating = False
Dim rng As Range, one As Variant
Dim i As Integer
'Change the sheet and range name as yours
'Finding lastrow of destination column
i = Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row + 1
' getting input from user
Set rng = Application.InputBox("Please select a range of cells!", "Please select a range", Selection.Address, , , , , 8)
For Each one In rng
If one.Value <> "" Or one.Value <> 0 Then
Range(one.Offset(0, -1), one).Copy
'Change the sheet and range name as yours
Sheets("Sheet2").Activate
Range("A" & i).Select
ActiveSheet.Paste
i = i + 1
End If
Next one
Application.ScreenUpdating = True
End Sub
The above macro will prompt you for the input range to be validate and copy to sheet2 in column A.
The below code will validate and copy paste the current selected range to sheet2 column A
Sub test()
Application.ScreenUpdating = False
Dim rng As Range, one As Variant
Dim i As Integer
'Chnage the sheet and range name as yours
'Finding lastrow of destination column
i = Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row + 1
' getting input from user
Set rng = Selection
For Each one In rng
If one.Value <> "" Or one.Value <> 0 Then
Range(one.Offset(0, -1), one).Copy
'Chnage the sheet and range name as yours
Sheets("Sheet2").Activate
Range("A" & i).Select
ActiveSheet.Paste
i = i + 1
End If
Next one
Application.ScreenUpdating = True
End Sub
I think you're probably going about this the wrong way. Do you already know to where you would like to copy all the data in the end? It sounds like it, as you refer to copying it "to a specific row". If so, you'd be better off using your macro to copy the data from Columns A:B on the fly.
So, for example:
Sub CopyData()
Const SOURCE_COLUMN1 As Long = 1 ' A
Const SOURCE_COLUMN2 As Long = 2 ' B
Const TARGET_COLUMN1 As Long = 5 ' E
Const TARGET_COLUMN2 As Long = 6 ' F
Dim lngSourceRow As Long
Dim lngTargetRow As Long
With ThisWorkbook.Sheets("Sheet1")
lngSourceRow = 1
lngTargetRow = 0 ' Change this to the row above the one you want to copy to;
Do While .Cells(lngSourceRow, SOURCE_COLUMN1) <> ""
If .Cells(lngSourceRow, SOURCE_COLUMN2) <> "" Then
lngTargetRow = lngTargetRow + 1
.Cells(lngTargetRow, TARGET_COLUMN1) = .Cells(lngSourceRow, SOURCE_COLUMN1)
.Cells(lngTargetRow, TARGET_COLUMN2) = .Cells(lngSourceRow, SOURCE_COLUMN2)
End If
lngSourceRow = lngSourceRow + 1
Loop
End With
End Sub

(Excel) How Can I Add Worksheet Name as Prefix for Each Column Header?

I have a header that starts in Column E and might go on for 100+ columns.
I am trying to change each column header to add a prefix (the name of the "tab" aka. worksheet) (ie. if Worksheet is called 'Beverage', I'd like each column header to be prefixed with "Beverage -")
I will be running script across multiple sheets, so am trying to find a way to reference the current sheet name.
Before: (For Worksheet "Beverage")
After: (For Worksheet "Beverage". Note: Columns don't need to be resized, just did it to demonstrate)
I've tried adapting code from this thread, however I can't get it to work.
Here is the code I have so far (non-working):
Sub Worksheet_Name_Prefix()
Dim columnNumber As Long, x As Integer
Dim myTab As ListObject
Set myTab = ActiveSheet.ListObjects(rows.Count, 1)
For x = 5 To rows.Count ' For Columns E through last header cell with value
columnNumber = x
myTab.HeaderRowRange(1, columnNumber) = ActiveSheet.Name
Next x
End Sub
Any suggestions on what's wrong with my code? Any help would be greatly appreciated.
I hope this help you...
Sub Worksheet_Name_Prefix_v2()
Dim h 'to store the last columns/header
Dim rngHeaders As Range 'the whole range with the headers from E1 to x1
Dim i 'just and index
Dim sht As Worksheet 'the sheet where you want the job done
h = Range("E1").End(xlToRight).Column 'find the last column with the data/header
Set rngHeaders = Range(Cells(1, 5), Cells(1, h)) 'the range with the headers E = column 5
'Cells 1 5 = E1
'Cells 1 h = x1 where x is the last column with data
Set sht = ActiveSheet 'the sheet with the data, _
'and we take the name of that sheet to do the job
For Each i In rngHeaders 'for each cell in the headers (every cells in row 1)
i.Value = sht.Name & " - " & i.Value
'set the value "sheet_name - cell_value" in every cell
Next i
End Sub
If you need any emprovement please tell me... I'm not sure if I get the real idea of what you need.
Edit #1
Use this in a regular module:
Option Explicit
Sub goForEverySheet()
Dim noSht01 As Worksheet 'store the first sheet
Dim noSht02 As Worksheet 'store the second sheet
Dim sht 'just a tmp var
Set noSht01 = Sheets("AA") 'the first sheet
Set noSht02 = Sheets("Word Frequency") 'the second sheet
appTGGL bTGGL:=False
For Each sht In Worksheets ' for each sheet inside the worksheets of the workbook
If sht.Name <> noSht01.Name And sht.Name <> noSht02.Name Then
'IF sht.name is different to AA AND sht.name is diffent to WordFrecuency THEN
'TIP:
'If Not sht.Name = noSht01.Name And Not sht.Name = noSht02.name Then 'This equal
'IF (NOT => negate the sentence) sht.name is NOT equal to noSht01 AND
' sht.name is NOT equal to noSht02 THEN
sht.Activate 'go to that Sheet!
Worksheet_Name_Prefix_v3 'run the code
End If '
Next sht 'next one please!
appTGGL
End Sub
Sub Worksheet_Name_Prefix_v3()
Dim h 'to store the last columns/header
Dim rngHeaders As Range 'the whole range with the headers from E1 to x1
Dim i 'just and index
Dim sht As Worksheet 'the sheet where you want the job done
h = Range("E1").End(xlToRight).Column 'find the last column with the data/header
Set rngHeaders = Range(Cells(1, 5), Cells(1, h)) 'the range with the headers E = column 5
'Cells 1 5 = E1
'Cells 1 h = x1 where x is the last column with data
Set sht = ActiveSheet 'the sheet with the data, _
'and we take the name of that sheet to do the job
For Each i In rngHeaders 'for each cell in the headers (every cells in row 1)
i.Value = sht.Name & " - " & i.Value
'set the value "sheet_name - cell_value" in every cell
Next i
End Sub
Public Sub appTGGL(Optional bTGGL As Boolean = True)
Debug.Print Timer
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
End Sub
Your code was not running because, you do not use this line sht.Activate you say, for every sheet in the workbook do this, but you not say to go to every sheet, and the the code run n times in the same sheet (as many sheets there in the workbook less two). But if you say, for every sheet do this, AND got to each of one of that sheets and do this (less that two sheets) you will get whay you want