I am trying to make a macro that selects certain data in my sheet. I have a sheet with data that is pulled into it using:
Windows("Item checkout workbook_New.xlsx").Activate
Range("A2:G300").Select
Selection.Copy
Windows("VLookup test.xlsx").Activate
Sheets("Sheet1").Select
Range("A2:G2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Sheets("Sheet1").Range("A2:G300").Copy Sheets("Sheet2").Range("A2")
Sheets("Sheet2").Select
Application.CutCopyMode = False
Once this data is input, I have two columns H2:H300 and I2:I300 that has formulas already in it for Vlookup that get information from A2:G300.
What I then need to do is select only the relevant data and copy it back to Windows("Item checkout workbook_New.xlsx"). By relevant data, I need to select only cells with data in the A2:G300 range as well as the H2:I300 cells that match. Seeing as ALL H2:I300 cells have data, I am not sure how to do this. I tried to create a macro that uses END to select all of column A and then the rows that go with it, but this is what I got and as you can see it will not work:
Range("A2").Select
Range(Selection, Selection.End(xlDown)).Select
Range("A2:I78").Select
Selection.Copy
I am not great at VBA thus it is hard to come up with things on the fly, but I feel like there should be a way to get this to work. Any advice would be great!
Range("A2").Select
Range(Selection, Selection.End(xlDown)).EntireRow.Select
Selection.Copy
Windows("Item checkout workbook_New.xlsx").Activate
Sheets("Sheet1").Select
Range("A2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Got it working!
Based off your own answer, it seems I may have misinterpreted your question. What I gathered was that you were looking for a way to select relevant cells in a worksheet. The relevant cells could belong to one of two ranges. In one range, cells that are not empty should be selected. In the other range, cells that match a value should be selected. (you can just add in your copy/paste code)
I solved that problem below.
Sub test()
'store results here
Dim result As Range
setupTest
'check this range and return items that are not empty
selectMatchingCells Range("A1:D1"), result
'check this range and return items that match value
selectMatchingCells Range("B2:C4"), result, "hi"
result.Select
End Sub
Function setupTest()
Range("A1").Value = "anything"
Range("c1").Value = "may go"
Range("D1").Value = "here"
Range("B2").Value = "hi"
Range("B3").Value = "but not here"
End Function
Function selectMatchingCells(search As Range, result As Range, Optional searchValue As String = "")
For Each cell In search
'are we checking that cell value matches string, or if cell has a value at all?
If searchValue = vbNullString Then
'check if cell is not empty
If IsEmpty(cell) = False Then selectCell result, cell
Else
'check if value matches
If cell.Text = searchValue Then selectCell result, cell
End If
Next cell
End Function
Function selectCell(result As Range, cell As Variant)
'check if result range already exists or not
If result Is Nothing Then
'make range equal to cell
Set result = cell
Else
'add cell to existing range
Set result = Union(result, cell)
End If
End Function
Please be more clear to avoid miscommunication, thanks!
Related
I want to copy the amount and paste it to all empty cells above the amount as shown in the picture , using VBA or Macro in MS Excel
Edit:
This is what I done it.
Sub Macro2()
Range("F5").Select
Selection.Copy
Range("F2:F4").Select
ActiveSheet.Paste
Range("F7").Select
Application.CutCopyMode = False
Selection.Copy
Range("F6").Select
ActiveSheet.Paste
Range("F12").Select
Application.CutCopyMode = False
Selection.Copy
Range("F8:F11").Select
ActiveSheet.Paste
End Sub
This code only works on this table only. If contents change it's not working. eg: if more items added, it's giving different outputs.
Can anyone help me to resolve it. Thanks in advance.
As far as the logic of the copying of values, I think this is something you need to work out. But I do have some hints of how to get there.
Do NOT use Select/Copy/Paste - Use direct assignment instead
Range("G5").Resize(3, 1).Value = Range("F5").Value
This will take the one value in cell F5 and use it to assign the three cells G5:G7.
To pick a specific value in a table, use the .Cells() function
For i=1 to 10
Range("B2").Cells(i,1).Value = Range("A2").Cells(i,1).Value
Next i
To count the number of rows down, or column across that have values use the following functions (placed in a module).
Public Function CountCols(ByVal r As Range) As Long
If IsEmpty(r) Then
CountCols = 0
ElseIf IsEmpty(r.Offset(0, 1)) Then
CountCols = 1
Else
CountCols = r.Worksheet.Range(r, r.End(xlToRight)).Columns.Count
End If
End Function
Public Function CountRows(ByVal r As Range) As Long
If IsEmpty(r) Then
CountRows = 0
ElseIf IsEmpty(r.Offset(1, 0)) Then
CountRows = 1
Else
CountRows = r.Worksheet.Range(r, r.End(xlDown)).Rows.Count
End If
End Function
To be used as
Dim n as Long
' Count rows in table starting from A2
n = CountRows(Range("A2"))
Dim r as Range
' Set a range reference to nĂ—1 cells under A2
Set r = Range("A2").Resize(n,1)
To check if a cell is empty, use IsEmpty() just like I have used in CountRows().
Can you piece all these pieces together to make a macro that does what you want? You should: a) check how many rows are in the table, b) go down the cells and check if they are empty in the destination and c) if they are, copy from the same row in the source.
I want to stop my loop if the current selection is empty. I have tried the following:
If (IsEmpty(Sheets("Sheet3").ActiveCell)) Then Exit Do
If Sheets("Sheet3").Selection.Value = "" Then Exit Do ;tried to replace "" with Empty and Nothing but didn't work either
If Sheets("Sheet3").Activecell.Value is Empty Then Exit Do
If Sheets("Sheet3").Selection is blank Then Exit Do
The issue is if I don't stop the loop somehow it will carry on forever.
I was hoping somebody can help me here.
EDIT :
This is my code:
Public Sub CopyFilteredData()
Do
Sheets("Sheet4").Select
ActiveSheet.Range("$A$1:$R$25239") _
.AutoFilter _
Field:=5, _
Criteria1:=Sheets("Sheet3").Application.Selection.Value
Range("A1").Select
ActiveCell.CurrentRegion.Select
Selection.Copy
Sheets("Sheet5").Select
Range("A1").Select
ActiveCell.End(xlDown).Select
Selection.Offset(1, 0).Select
ActiveSheet.Paste
Range("A1").Select
ActiveCell.End(xlDown).Select
Selection.Offset(1, 0).Select
ActiveCell.FormulaR1C1 = "+"
Sheets("Sheet3").Select
Selection.Offset(1, 0).Select
' This is where the code to stop the loop needs to go
Loop
End Sub
This question is a classic case of the XY Problem.
Y Solution
The main reason none of your four attempts to detect an empty cell work, is a lack of understanding on what Selection and ActiveCell actually are. They are properties of the Application object and return the following
Selection - the selected object of the active sheet (the top most sheet)
ActiveCell - the active cell of the active sheet (the top most sheet)
You can't use Sheets("Sheet3").ActiveCell or Sheets("Sheet3").Selection as the Sheet object doesn't have these properties.
What you can use is Application.ActiveCell and Application.Selection or, more simply, ActiveCell and Selection. Of course, this will only work after activating Sheet3.
My preferred way of doing this is:
Sheets("Sheet3").Activate
If (IsEmpty(ActiveCell)) Then Exit Do
Your code also contains a similar problem with this bit:
Criteria1:=Sheets("Sheet3").Application.Selection.Value
While the code correctly gets the Selection object, it doesn't actually activate Sheet3 and is exactly the same as writing:
Criteria1:=Application.Selection.Value or Criteria1:=Selection.Value
Fixing this issue by storing the Sheet3 selection value in a variable leads to the following working code:
Option Explicit
'(v0.2)
Public Sub Y_Fixed_BUT_VERY_VERY_VERY_BAD_CODE()
' Added three lines and changed a fourth to fix the incorrect usage of "Selection" for the criteria
' Changed a fifth line to add the correct loop exit code
Sheets("Sheet3").Activate ' Fix#1 Not necessary if the code is always run from Sheet3
Dim varSheet3ActiveCellValue As Variant ' Fix#2
Do
varSheet3ActiveCellValue = ActiveCell.Value2 ' Fix#3
Sheets("Sheet4").Select
ActiveSheet.Range("$A$1:$R$25") _
.AutoFilter _
Field:=5, _
Criteria1:=varSheet3ActiveCellValue ' Fix#4
Range("A1").Select
ActiveCell.CurrentRegion.Select
Selection.Copy
Sheets("Sheet5").Select
Range("A1").Select
ActiveCell.End(xlDown).Select
Selection.Offset(1, 0).Select
ActiveSheet.Paste
Range("A1").Select
ActiveCell.End(xlDown).Select
Selection.Offset(1, 0).Select
ActiveCell.FormulaR1C1 = "+"
Sheets("Sheet3").Select
Selection.Offset(1, 0).Select ' Fix#5
If IsEmpty(ActiveCell) Then Exit Do
Loop
End Sub
X Solution
As mentioned in response to your first posted question, you really, really need to learn how to avoid using .Select. This Stack Overflow post is a good place to start.
The following code is the equivalent to the above, without using a single .Select, .Activate, Selection, or ActiveCell. It also includes a better way to find the last value in a column. (Your method fails unless there is a least one cell containing a value after the first cell.)
A useful way to work out how the code works is to select a word in it, for example With and pressing F1. This will bring up the Excel Help related to that word, with explanations and examples.
'============================================================================================
' Module : <in any standard module>
' Version : 1.0
' Part : 1 of 1
' References : N/A
' Source : https://stackoverflow.com/a/47468132/1961728
'============================================================================================
Option Explicit
Public Sub X__GOOD_CODE()
Dim rngFilterCriteriaList As Range
With Sheets("Sheet3").Range("A3")
Set rngFilterCriteriaList = Range(.Cells(1), .EntireColumn.Cells(Rows.Count).End(xlUp))
End With
Dim rngCell As Range
For Each rngCell In rngFilterCriteriaList
Sheets("Sheet4").Range("A1:R25239") _
.AutoFilter _
Field:=Range("E:E").Column, _
Criteria1:=rngCell.Value2
Sheets("Sheet4").Range("A1").CurrentRegion.Copy _
Destination:=Sheets("Sheet5").Range("A:A").Cells(Rows.Count).End(xlUp).Offset(1)
Sheets("Sheet5").Range("A:A").Cells(Rows.Count).End(xlUp).Offset(1).Value2 = "+"
Next rngCell
Sheets("Sheet4").Cells.AutoFilter
End Sub
if u cannot specify the range then have to activated sheet3 then its works refer below:
ThisWorkbook.Worksheets("Sheet3").Activate
If ActiveCell = "" Then
Exit Do
End If
Your selection will not change if you are not changing the cell by using .select in the code and therefore will likely result in infinite loop. But using .select in the code is not considered as good practice as it slows down the process.
I'd suggest using For...each Loop like below.
Dim rng as Range
For each rng in selection
If Len(rng.Value) = 0 then Exit Sub '\\ Exit at first blank cell
'\\ Do process here
Next rng
selection can contain 1 or more cells. If you want to check if all the cells in the selection are empty you can use the worksheet function countblank which returns the number of empty cells. If the number of empty cells in the selection equals the number of cells in the selection then all the cells in the selection are empty. your test can be adapted like this
If Application.WorksheetFunction.CountBlank(Selection) = Selection.Count Then Exit Do
Your solution is here.
Credits to mvptomlinson from MrExcel.com
The right code is
'Your code to loop through copying sheets
If ActiveSheet.Range("A1").Value = "" Then Exit Sub
'Your code to continue if A1 isn't empty
I have a worksheet containing data. As soon as something changes in a specific column, I want to copy the values of one column in this sheet to another worksheet, but only rows which match some criteria. So I have auto-filtered a range. This works. It only returns rows matching the filter. But from this filtered range, I only need one column. Somehow I cannot get this to work.
So my question would be, how can I only copy a specific column from a filtered range?
Code (snipped) I have so far:
Me.AutoFilterMode = False
With Me.Range("C4:D103")
.AutoFilter Field:=2, Criteria1:="=Marge Only", Operator:=xlOr, Criteria2:="=Contracting"
.SpecialCells(xlCellTypeVisible).Copy Destination:=ThisWorkbook.Worksheets("Result").Range("B5:B104")
End With
ThisWorkbook.SortResult
On Error Resume Next
Me.AutoFilterMode = False
Me.ShowAllData
On Error GoTo 0
The .SpecialCells(xlCellTypeVisible).Copy part copies too much data to the destination worksheet. I need something like:
.Range("A:A").SpecialCells(xlCellTypeVisible).Copy
With .Range("A:A") my thought would be that only column A from the already filtered range would be copied. But this doesn't work.
So what would be your advice how to accomplish this?
You can modify your code slightly to copy only the column you need. This code assumes column A (but you can adjust) and it assumes row 4 is header data (you can also adjust.
With Me
.Range("C4:D103").AutoFilter Field:=2, Criteria1:="=Marge Only", Operator:=xlOr, Criteria2:="=Contracting"
.Range("A5:A103").SpecialCells(xlCellTypeVisible).Copy Destination:=ThisWorkbook.Worksheets("Result").Range("B5")
End With
Is this what you are talking about? It checks column "I" for the criterial then it finds the first and last cells in a filter "A" column and copies the values between the two and paste it in column "O"
Sub copyColumn()
Dim StrRow As Long
Dim str As String
Dim str2 As String
Dim str3 As String
With Sheet1
.AutoFilterMode = False
With .Range("A1:M1")
.AutoFilter
.AutoFilter Field:=9, Criteria1:="dog"
StrRow = Sheets("Sheet1").AutoFilter.Range.Offset(1).SpecialCells(xlCellTypeVisible).Cells(1, 2).Row
str = .Range("A" & StrRow).Address
str2 = .Range("A1").End(xlDown).Address
.Range("O2:O" & str3).Value = .Range(str, str2).Value
End With
End With
End Sub
Saved a Macro where I would go to to the last non-empty cell in a row by using CTRL+Right Arrow.
The macro, however, was saved by logging the J23 cell.
I want it to be dynamic so that if last non-empty cell changes, for instance, it is N23, it jumps there.
I would like that my other steps would then always link to cells L30, M30, N30 and O30 but in this case they are using relative coordinates (I tried to edit the macro from J23 to N23, for instance, and the linked cells change)
What are the code lines that I can use to make it dynamic the way I need?
Thank you for your help
Sub KPILinks()
'
' KPILinks Macro
'
'
Range("I22").Select
Selection.End(xlToRight).Select
Range("J23").Select
ActiveCell.FormulaR1C1 = "=R[7]C[2]"
Range("J24").Select
ActiveCell.FormulaR1C1 = "=R[6]C[3]"
Range("J25").Select
ActiveCell.FormulaR1C1 = "=R[5]C[4]"
Range("J26").Select
ActiveCell.FormulaR1C1 = "=R[4]C[5]"
Range("J27").Select
End Sub
borrowed from https://www.rondebruin.nl/win/s9/win005.htm
Sub LastRowInOneColumn()
'Find the last used row in a Column: column A in this example
Dim LastRow As Long
With ActiveSheet
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
MsgBox LastRow
End Sub
New to this forum so sorry if this is off. I'm trying to do a simple copying of cell values from one worksheet in a book to another worksheet, but need to use relative cell references as the number of rows that will be copy/pasted changes depending on the data inputted.
The (very simple) code so far is:
Sub SuitorList()
'Defining Variables
Dim Row As Integer
Row = Sheets("References").Cells(6, 2).Value
'Copying Statistics
Sheets("Charts").Range(Cells(1, 1), Cells(Row, 1)).Value = _
Sheets("Data").Range(Cells(1, 1), Cells(Row, 1)).Value
End Sub
This code works fine when I use absolute cell references (i.e. "B1:B7") but when I use a relative reference I receive error code 1004: Application-defined or object-defined error.
Any thoughts?
Alternative Solution:
If you are not a fan of Loops, use Worksheet.Cells Property
Sub SuitorList()
'Defining Variables
Dim Row As Integer
Set wd = ThisWorkbook.Worksheets("Data")
Set wc = ThisWorkbook.Worksheets("Charts")
Row = Sheets("References").Cells(6, 2).Value
'Copying Statistics
Range(wd.Cells(1, 1), wd.Cells(Row, 1)).Copy Destination:=Range(wc.Cells(1, 1), wc.Cells(Row, 1))
End Sub
If you are copying data from one sheet to another and the amount of data to be copied/pasted is always changing then I would do something like this. Which is filtering the data from your selection sheet then copying it and pasting it to your destination sheet by finding the first blank cell. You may have to mess with this a bit, but it is a good start.
'Defining Variables
Dim Row As Integer
Row = Sheets("References").Cells(6, 2).Value
'switches the sheet
Sheets("Charts").Select
'filters a table based on the value of the Row variable
ActiveSheet.ListObjects("Table1").range.AutoFilter Field:=1, Criteria1:= _
range("Row"), Operator:=xlAnd
'moves to the first cell in the filtered range
range("A1").Select
'selects all values in the range and copies to clipboard
range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
'switches the sheet back to data sheet
Sheets("Data").Select
'finds the first blank cell in the declared range you want to paste into
ActiveSheet.range("A:A").Find("").Select
'pastes the selection
ActiveSheet.Paste
Thanks for the help. I was able to find a work around using the following code:
Sub SuitorList()
'Defining Variables
Dim Row As Integer
Row = Sheets("References").Cells(6, 2).Value
'Copying Statistics
For i = 1 To Row
Sheets("Charts").Range("A" & i).Value = Sheets("Data").Range("A" & i).Value
Next
End Sub