How to copy only visible/filtered array values to clipboard? - excel

So I have a worksheet of data and I want to copy a comma-delimited array to my clipboard. If I have to paste the value into a cell first, that is fine as well. The worksheet has autofilters on and is filtered. I only want to select the values that are currently visible due to the filtering, not the whole array.
The array is in column P and starts in P2. I have a LastRow set up, and have been able to get the comma-delimited part to work, but am having trouble with the copying to clipboard part and the visible values only part.
The code below creates the comma-delimited list and I can show it in a message box or something, but I'm not sure how to copy it to the clipboard or how to make sure only visible values are being selected.
Dim LastRow As Long
LastRow = Range("P" & Rows.Count).End(xlUp).Row
Dim arr
arr = Join(Application.Transpose(Range("P2:P" & LastRow).Value), ",")

Try this code
Sub Test()
Dim arr, rng As Range, c As Range, n As Long
Set rng = Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
ReDim a(1 To rng.Cells.Count)
For Each c In rng
n = n + 1: a(n) = c.Value
Next c
arr = Join(a, ",")
End Sub

Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)

Related

Load a variable range into an array

I want to store a range of variable size in an one-dimensional array. The range starts at A2 and goes to the last row of the same column. My approach looks like that. It's flawed.
Option Explicit
Sub Range_to_Array()
Dim i, j, k As Integer
Dim arr1(), arr2(), arr3(), arr4() As Variant
With Worksheets("table1")
arr1() = .Cells(.Range("A2"), .Range("A1").End(xlDown).Row)
End With
End Sub
Please, try the next way. Application.Transpose transforms a 2D array with a column in a 1D type. No iteration needed:
Sub Array1DFromColumnRange()
Dim ws As Worksheet, lastR As Long, arr
Set ws = Worksheets("table1")
lastR = ws.Range("A" & ws.rows.count).End(xlUp).Row
arr = Application.Transpose(ws.Range("A2:A" & lastR).Value)
Debug.Print Join(arr, "|") 'just to visually see the result in Immediate Window (Ctrl + G)...
End Sub
The returned 1D array is 1 based, as the 2D array directly extracted from a range. To transform it in zero based type, can be done without iteration, too:
'change the array type to be zero based:
ReDim Preserve arr(0 To UBound(arr) - 1)
Debug.Print LBound(arr)
Debug.Print Join(arr, "|")
Your problem is that your Range-Definition is wrong.
Cells expect 2 parameters (row and column) to address one cell. This is not what you want, and even if, your parameters would be wrong.
What you need in your case is Range.
Now Range can be called either with one or two parameters.
If you call it with one parameter, this defines the whole range.
Examples: Range("A1") or Range("B2:C5") or Range("B:B")
Whats often used in VBA is something like Range("A1:A" & lastRow)
If you call it with two parameters, those two parameters define the first and last cell of the range.
Examples: Range(Range("A1"), Range("C10")) or Range(Cells(1, 1), Cells(10, 3))
I would advice to define an intermediate variable to save the Range - makes it much easier to debug. Also the row number of the last cell should go into an intermediate variable.
In your case you could use one of the following
Dim r As Range, lastRow As Long
' Get the last row in use (usually its better to go up from the end of the sheet)
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
' 2 parameters
Set r = .Range(.Cells(2, 1), .Cells(2, lastRow))
' 1 Parameter, last row is concatenated to the range definition
Set r = .Range("A2:A" & lastRow)
' Use Resize
Set r = .Range("A2").Resize(lastRow-1, 1) ' -1 because you start at row 2
arr1 = r.Value
Try this instead.
Sub Range_to_Array()
Dim i As Integer, j As Integer, k As Integer
Dim arr1() As Variant, arr2() As Variant, arr3() As Variant, arr4() As Variant
Dim myRange As Range
With Worksheets("table1")
arr1 = .Range(.Range("A2"), .Range("A1").End(xlDown)).Value
End With
Debug.Print arr1(1, 1)
End Sub
Also please note that in order to properly declare variables, you need to specify data type for each variable separately.
Dim i, j, k As Integer
actually means
Dim i As Variant, j As Variant, k As Integer

In VBA code,how to add values to a listbox using for each cell approach with defining a range End(XlDown)?

I have created a macro file with Forms and Word to Excel.
In this coding fewthings are not working as per my expectation.
Get unique Employee Name from Excel data base.
I want to add unique employee names from excel database and get is saved in a sheet. After that those values to be added to list box. Here i cannot define a range like "A1:A10".. I want to choose the data from A1 to end data.
If for each cell approach will not work, please help in do while approach
I need help in defining the range and code given below
ListEmployeeName.Clear
For Each cell In Worksheets("SunEmployeeDetails").Range("A1").End(xlDown)
ListEmployeeName.AddItem (cell.Value)
Next
ListEmployeeName.Value = Worksheets("SunEmployeeDetails").Range("A1")
End Sub
Find Last Row and then define your range Range("A1:A" & LastRow)
You can also find the last row and loop through the range using a For loop. Also to get unique Employee Name, you can use On Error Resume Next with a Collection as shown below. I have commented the code below so you should not have a problem understanding it. But if you do then simply ask.
Is this what you are trying? (Untested).
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Dim col As New Collection
Dim itm As Variant
Set ws = Worksheets("SunEmployeeDetails")
With ws
'~~> Find Last row
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Loop through the range and add it to the unique
'~~> collection using "On Error Resume Next"
For i = 1 To lRow
On Error Resume Next
col.Add .Range("A" & i).Value, CStr(.Range("A" & i).Value)
On Error GoTo 0
Next i
End With
ListEmployeeName.Clear
'~~> add the itme from collection to the listbox
For Each itm In col
ListEmployeeName.AddItem itm
Next itm
End Sub
Here is my take on it, techniques taken from here:
Methode 1: Using a dictionary
Dim lr As Long, x As Long
Dim arr As Variant
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
With Sheet1 'Change accordingly
'Find the last used row
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A1:A" & lr).Value
End With
'Loop through memory and fill dictionary
For x = LBound(arr) To UBound(arr)
dict(arr(x, 1)) = 1
Next x
'Add array to Listbox
Me.ListEmployeeName.List = dict.Keys
Methode 2: Using Evaluation
Dim lr As Long
Dim arr As Variant
With Sheet1 'Change accordingly
'Find the last used row
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
'Get array of unique values
arr = Filter(.Evaluate("TRANSPOSE(If(Row(A1:A" & lr & ")<>MATCH(A1:A" & lr & ",A1:A" & lr & ",0),""|"",A1:A" & lr & "))"), "|", False)
'Add array to Listbox
Me.ListEmployeeName.List = arr
End With

I have to run my code several times for it to execute entirely

I'm not sure whether it's because I'm using a mac or the code is wrong, but the rows aren't identifying properly, and therefore not deleting or pasting it into the other spreadsheet. I have to run the code three times for it to properly go through it and copy/paste and delete the cells into the other spreadsheet.
Many thanks!
here is the code:
Dim j, lastidno As Long
Sheets("Part B + C Modules").Activate
lastidno = Range("A2", Range("A2").End(xlDown)).Count + 1
For j = 2 To lastidno
If Range("O" & j) = "" Then
Sheets("Part B + C Modules").Range("A" & j).Copy
Sheets("No Options Selected").Select
NextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(NextRow, 1).Select
ActiveSheet.Paste
Sheets("Part B + C Modules").Activate
Rows(j).EntireRow.Delete
End If
Next
MsgBox "done"
End Sub
Iteration and deleting rows goes backwards using a negative Step > For j = lastidno to 2 Step -1
However, it appears you could rewrite your code a bit more elegantly to avoid:
Implicit Range references
Iteration
Use of Activate or Select
The key is to have Explicit sheet references to work with. Also the use of SpecialCells can come in handy here to return a Range in one go (so no more iteration). This way you can also delete all rows in one go!
You code could, for example, look like:
Sub Test()
'Set up your worksheet variables
Dim ws1 As Worksheet: Set ws1 = Worksheets("Part B + C Modules")
Dim ws2 As Worksheet: Set ws2 = Worksheets("No Options Selected")
'Get last used rows
Dim lr1 As Long: lr1 = ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row
Dim lr2 As Long: lr2 = ws2.Cells(ws2.Rows.Count, 1).End(xlUp).Row
'Set your range and copy it
Dim rng As Range: Set rng = ws1.Range("O2:O" & lr1).SpecialCells(xlCellTypeBlanks).Offset(0, -14)
rng.Copy ws2.Cells(lr2 + 1, 1)
'Delete your range
rng.EntireRow.Delete
MsgBox "done"
End Sub
Small catch: SpecialCells will return an error when no empty cells are found. You might want to work your way around that using On error or count the empty cells in your Range first (my personal preference). So that specific part could looke like:
'Set your range and copy it
If WorksheetFunction.CountBlank(ws1.Range("O2:O" & lr1)) > 0 Then
Dim rng As Range: Set rng = ws1.Range("O2:O" & lr1).SpecialCells(xlCellTypeBlanks).Offset(0, -14)
rng.Copy ws2.Cells(lr2 + 1, 1)
End If
Another small note for future reference: Dim j, lastidno As Long only has lastidno declared as Long data type. j Variable is auto-assigned to Variant/Integer so could potentially become a problem when your data is larger than this data type can hold > Return an OverFlow error.

VBA VLOOKUP with dynamic range

I am brand-new to VBA.
I have two worksheets in the same workbook. The first worksheet, shStudentInfo, contains all of the information for each of my students, one row per StudentID (B4 in the code). The second worksheet, shSchedData, contains their schedules where there may be 0-14 rows per StudentID, depending on how many courses each student is taking.
I am attempting to use a loop and VLOOKUP with a dynamic range to extract the course name from each row of shSchedData and copy it to the appropriate cell in shStudentInfo, then move down one row. Currently I've hardcoded cell "CO4" as the appropriate cell although I will also need to make that reference move one cell to the right for each pass through the loop.
Here is my inelegant code:
Option Explicit
Dim MyRow As Long
Sub StudentSchedules()
Dim EndRow As Long
Dim MyRng As Range
shSchedData.Activate
'hard code first row of data set
MyRow = 3
'dynamic code last row of data set
EndRow = shSchedData.Range("A1048575").End(xlUp).Row
'create a dynamic range, a single row from shSchedData
Set MyRng = ActiveSheet.Range(Cells(MyRow, 1), Cells(MyRow, 9))
'Loop through entire data set one line at a time
Do While MyRow <= EndRow
shSchedData.Select
MyRng = ActiveSheet.Range(Cells(MyRow,1),Cells(MyRow,9))
shStudentInfo.Select
'Import course name from shSchedData worksheet
Range("CO4").Select
ActiveCell.Clear
ActiveCell.Formula = "=VLOOKUP(B4,'Schedule Data'!& MyRng,6,0)"
'The above line results in a #NAME? error in CO4 of shStudentInfo
'Also tried:
'ActiveCell.Formula = "=VLOOKUP(B4,'Schedule Data'!& MyRng.Address,6,0)"
'increment counter
MyRow = MyRow + 1
Loop
End Sub
The following rewrite will get your code working to the extent that its purpose can be determined.
The VLOOKUP formula does not appear correct and in any event, there might be a better method of retrieving the data. However, I cannot determine your end purpose from your narrative or code. Sample data together with expected results would help.
Option Explicit
'I see no reason to put this here
'dim myRow As Long
Sub StudentSchedules()
Dim myRow, endRow As Long, myRng As Range
'no need to activate, just With ... End With block it
With shSchedData
'assigned a strarting value
myRow = 3
'dynamic code last row of data set
endRow = .Cells(.Rows.Count, "A").End(xlUp).Row
'Loop through entire data set one line at a time
Do While myRow <= endRow
'create a dynamic range, a single row from shSchedData
Set myRng = .Range(.Cells(myRow, 1), .Cells(myRow, 9))
'Import course name from shSchedData worksheet
shStudentInfo.Range("CO4").Offset(0, myRow - 3).Formula = _
"=VLOOKUP(B4, " & myRng.Address(external:=True) & ", 6, false)"
'increment counter
myRow = myRow + 1
Loop
End With
End Sub
I came up with this, see if it fits you
Dim a As Double
Dim b As Double
Dim ml As Worksheet
Dim arrayrng As Variant
Dim i As Integer
Dim x As String
Dim y As String
Set ml = Worksheets("Master Data")
a = ml.Cells(Rows.Count, 11).End(xlUp).Row
b = ml.Cells(Rows.Count, 1).End(xlUp).Row
For i = a To b - 1
a = ml.Cells(Rows.Count, 11).End(xlUp).Row
b = ml.Cells(Rows.Count, 1).End(xlUp).Row
arrayrng = "E" & a + 1
x = "=VLOOKUP(" & arrayrng
y = ",'Data Base'!I:J,2,0)"enter code here
Range("K" & a + 1) = x + y
Next

VBA iterate drop down list

I have a spread sheet with J1 being a drop down list. Content in row 8-14 would change based on what you choose in J1. I need to iterate through all values in the drop down list and copy all the corresponding rows to a new workbook. the copy paste part is working, but I am having problem iterating through the drop down list. particularly, I need some help to define Formula1. I am using excel 2010. Here is my code. Thanks in advance
Sub iterate_dropdown()
Dim inputRange As Range
Dim c As Range
Dim Current As Range
Set inputRange = Evaluate(Workbooks("sample.xlsm").Worksheets("Credit Research Journal").Range("J1").Validation.Formula1)
For Each c In inputRange
Workbooks("sample.xlsm").Worksheets("Credit Research Journal").Range("J1").Value = c.Value
Workbooks("sample.xlsm").Sheets("Credit Research Journal").Activate
Workbooks("sample.xlsm").RefreshAll
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
Cells(8, 1).Resize(FinalRow - 7, 10).Copy
Workbooks("Book2.xlsm").Sheets("Sheet3").Activate
NextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Set Current = Cells(NextRow, 1)
Current.PasteSpecial xlPasteValues
Next c
End Sub
I did some experimenting with the code you posted, and it seems you were pretty close. I discovered that the step where you were getting inputRange was including an equals sign (=), which then caused the Evaluate function to fail.
Here is the code I used:
Sub iterate_dropdown()
Dim inputRange As Range
Dim c As Range
Dim Current As Range
Dim strRange As String
Dim strRange2 As String
strRange = Worksheets("Credit Research Journal").Range("J1").Validation.Formula1
strRange2 = Replace(strRange, "=", "") 'Get rid of the equals sign
Set inputRange = Evaluate(strRange2)
For Each c In inputRange
Workbooks("sample.xlsm").Worksheets("Credit Research Journal").Range("J1").Value = c.Value
Workbooks("sample.xlsm").Sheets("Credit Research Journal").Activate
Workbooks("sample.xlsm").RefreshAll
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
Cells(8, 1).Resize(FinalRow - 7, 10).Copy
Workbooks("Book2.xlsm").Sheets("Sheet3").Activate
NextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Set Current = Cells(NextRow, 1)
Current.PasteSpecial xlPasteValues
Next c
End Sub
I used two string variables strRange and strRange2 so that you can easily step through with the debugger and see what is happening.
Also I am assuming that your dropdown list is being populated by values that are references to other cells.

Resources