Copy a range to a virtual range - vba

Is it possible to copy a range to a virtual range or does it require me to sloppily paste it in another range in the workbook?
dim x as range
x = copy of Range("A1:A4")
obviously I usually use the following code
dim x as range
set x = Range("A1:A4")
but in the above example it only makes x a "shortcut" to that range rather than a copy of the range object itself. Which is usually what I want but lately I have been finding it would be quite useful to totally save a range and all it's properties in memory rather than in the workbook somewhere.

I think this is what you are trying to do:
'Set reference to range
Dim r As Range
Set r = Range("A1:A4")
'Load range contents to an array (in memory)
Dim v As Variant
v = r.Value
'Do stuff with the data just loaded, e.g.
'Add 123 to value of cell in 1st row, 3rd column of range
v(1,3) = v(1,3) + 123
'Write modified data back to some other range
Range("B1:B4").Value = v

Is it possible to copy a range to a virtual range?
No it is not possible. Range allways represents some existing instance(s) of cells on a worksheet in a workbook.
Does it require me to sloppily paste it in another range in the
workbook?
It depends on what you want to do. You can paste everithing from one range to another, you can paste only something like e.g. formulas to another range.
dim x as range
set x = Range("A1:A4")
But in the above example it only makes x a "shortcut" to that range
rather than a copy of the range object itself.
Variable x holds a reference to that specific range. It is not possible to made any standalone copy of a range. It is possible to create references to a range and to copy everithing / something from one range to another range.
Lately I have been finding it would be quite useful to totally save a
range and all it's properties in memory rather than in the workbook
somewhere.
Again, it is not possible to save all range properties to some virtual, standalone copy of specific Range because Range allways represents an existing, concrete set of cells. What you could do is to create your own class with some properties of a Range or even all properties ... but it will be some extra work to do.
Here some examples how to use range as parameter and copy it to another range. HTH.
Option Explicit
Sub Main()
Dim primaryRange As Range
Set primaryRange = Worksheets(1).Range("A1:D3")
CopyRangeAll someRange:=primaryRange
CopyRangeFormat someRange:=primaryRange
' Value property of a range represents and 2D array of values
' So it is usefull if only values are important and all the other properties do not matter.
Dim primaryRangeValues As Variant
primaryRangeValues = primaryRange.value
Debug.Print "primaryRangeValues (" & _
LBound(primaryRangeValues, 1) & " To " & UBound(primaryRangeValues, 1) & ", " & _
LBound(primaryRangeValues, 2) & " To " & UBound(primaryRangeValues, 2) & ")"
' Prints primaryRangeValues (1 To 3, 1 To 4)
Dim value As Variant
For Each value In primaryRangeValues
' This loop throught values is much quicker then to iterate through primaryRange.Cells itself.
' Use it to iterate through range when other properties except value does not matter.
Debug.Print value
Next value
End Sub
Private Sub CopyRangeAll(ByVal someRange As Range)
' Here all properties of someRange which can be copied are copied to another range.
' So the function gets a reference to specific range and uses all its properties for another range.
Dim secondaryRange As Range
Set secondaryRange = Worksheets(2).Range("D4:G6")
someRange.Copy secondaryRange
End Sub
Private Sub CopyRangeFormat(ByVal someRange As Range)
' Here only formats are copied.
' Function receives reference to specific range but uses only one special property of it in that another range.
Dim secondaryRange As Range
Set secondaryRange = Worksheets(3).Range("G7:J9")
someRange.Copy
secondaryRange.PasteSpecial xlPasteFormats ' and many more e.g. xlPasteFormulas, xlPasteValues etc.
End Sub

Related

Pasting range vba in values without using clipboard

I am trying to copy and paste a range in values without using the clipboard, below code works but doesn't copy in values (includes forumlas etc):
any ideas as how to make this work?
NbRowsPnLD1 = PnLD1WS.Range("A1").End(xlDown).Row
PnLD1WS.Range(PnLD1WS.Cells(1, 1), PnLD1WS.Cells(NbRowsPnLD1, 228)).Copy(PnLD2WS.Cells(1, 1)).PasteSpecial xlPasteValues
Copy the Values of a Range by Assignment
Option Explicit
Sub CopyByAssignment()
' It is assumed that 'PnLD1WS' and 'PnLD2WS' are the code names
' of two worksheets in the workbook containing this code.
' Calculate the last row,
' the row with the last non-empty cell in the column.
' Most of the time you want to use '.End(xlUp)' instead:
Dim slRow As Long
slRow = PnLD1WS.Cells(PnLD1WS.Rows.Count, "A").End(xlUp).Row
' ... because if you have empty cells in the column, it will reference
' the whole range regardlessly.
' The following will 'stop' at the first empty cell and may not reference
' the whole desired column range.
'slRow = PnLD1WS.Range("A1").End(xlDown).Row ' not recommended
' Reference the source range (the range to be copied from).
Dim srg As Range
Set srg = PnLD1WS.Range("A1", PnLD1WS.Cells(slRow, "HT"))
' Reference the destination range (the range to be written (pasted) to).
' Use 'Resize' to make it the same size as the source range.
Dim drg As Range
Set drg = PnLD2WS.Range("A1").Resize(srg.Rows.Count, srg.Columns.Count)
' Copy by assignment.
drg.Value = srg.Value
End Sub
Something like
With PnLD1WS.Range(PnLD1WS.Cells(1, 1), PnLD1WS.Cells(NbRowsPnLD1, 228))
PnLD2WS.Cells(1, 1).Resize(.Rows.Count,.Columns.Count).Value2 = .Value2
End With

Reference a Range using column number rather than column letter

I feel like this should be simple because I have found a million references online on how to do this. I've tried several different combos to no avail.
I am simply trying to reference a column using the LastCol variable within a range, but keep getting the error below. The error occurs on the line labeled with a ->. I've tried several different variations, including using the letter name for the first range paramter with a colon, comma, moving the quotes around, putting "." before the Cells references, etc. Every time I look up how to do this online, it seems like I am doing it right.
This is my code:
Public Sub cmb_orgname_Change()
Dim wb As Workbook
Dim sht As Worksheet
Dim orgname As String
Dim org_position As Double
Dim LastCol, LastRow As Integer
Dim prod_range As Range
Dim cell As Range
Set sht = Sheet1
'get the value of the selected org name from the dropdown
orgname = sht.OLEObjects("cmb_orgname").Object.Value
'find the last column for the number of org names, currently this is not being utilized
LastCol = Sheet3.Range("F2").End(xlToRight).Column
If orgname <> "" Then
Call Clear_ComboBox
-> org_position = WorksheetFunction.Match(orgname, Sheet3.Range(Cells(2, 6), Cells(2, LastCol)), 0) + 6
LastRow = Sheet3.Cells(Sheet3.Rows.Count, org_position).End(xlUp).Row
Set prod_range = Sheet3.Range(Sheet3.Cells(3, org_position), Sheet3.Cells(LastRow, org_position))
For Each cell In prod_range
With sht.OLEObjects("cmb_prodname").Object
Dim test As String
test = CStr(cell.Value)
.AddItem CStr(cell.Value)
End With
Next cell
End If
End Sub
after many different iterations of different searches, I found a function called ADDRESS() that comes after a range. With the ADDRESS function, I was able to make this work! Basically, all I had to do was change my line to the following:
org_position = WorksheetFunction.Match(orgname, Sheet3.Range("F2:" & Sheet3.Cells(2, LastCol).Address()), 0) + 6
And now it is running smoothly. This is the page I found the ADDRESS function and information on: https://software-solutions-online.com/use-vba-range-address-method/

How can I test if a selection is completely within a range?

So i have found this which is similar:
VBA test if cell is in a range
but this seems to be testing (as I understand it) if the cells selected intersect the range at all. However I need to find a way to confirm if the selected range is COMPLETELY within the range so that I can restrict the macro to only work inside a specified range of cells.
here is what I've got so far....I name the selected cells as a range (sel_rng) and set them as a variable....then I name the acceptable range as a named range (okay_rng)....then (hopefully....but this is the part I'm still unclear how to pull off) if "sel_rng" lies completely within "okay_rng" I want to grab "sel_rng" and merge it, otherwise throw up an error"
Sub Merge_Cells()
'
' Merge_Cells Macro
Dim selcells As Range
Selection.Name = "sel_rng"
selcells = Range("sel_rng")
Dim okayrng As Integer
okayrng = Range("itemrows").Value + 28
ActiveSheet.Range("C29:C" & okayrng).Select
Selection.Name = "okay_rng"
Range("sel_rng").Select
Selection.Merge
Thoughts anyone?
The intersection of the two ranges will determine if one range is completely within another range.
dim rng1 as range, rng2 as range
set rng1 = range("b2:c3")
set rng2 = range("a1:d4")
'if rng1 is completely within rng2, the intersection's address will be the same as rng1's address
if application.intersect(rng1, rng2).address = rng1.address then
debug.print rng1.address(0, 0) & " is within " & rng2.address(0, 0)
end if
btw, there is the possibility that the intersect could be nothing. You should add error handling for that.

Type Mismatch Error after MsgBox

my data is as below .
Updated Question
Sub Solution()
Dim shData As Worksheet
Set shData = Sheets("Sheet1") 'or other reference to data sheet
Dim coll As Collection, r As Range, j As Long
Dim myArr As Variant
Dim shNew As Worksheet
shData.Activate
'get unique values based on Excel features
Range("a1").AutoFilter
Set coll = New Collection
On Error Resume Next
For Each r In Range("A1:A10")
coll.Add r.Value, r.Value
Next r
On Error GoTo 0
'Debug.Print coll.Count
For j = 1 To coll.Count
MsgBox coll(j)
myArr = coll(j)
Next j
Range("a1").AutoFilter
Dim i As Long
For i = 0 To UBound(myArr)
shData.Range("$A$1").AutoFilter Field:=1, Criteria1:=myArr(i), _
Operator:=xlAnd
On Error Resume Next
Sheets(myArr(i)).Range("A1").CurrentRegion.ClearContents
If Err.Number = 0 Then
Range("A1").CurrentRegion.Copy Sheets(myArr(i)).Range("A1")
Else
Set shNew = Sheets.Add(After:=Sheets(Sheets.Count))
shData.Range("A1").CurrentRegion.Copy shNew.Range("A1")
shNew.Name = myArr(i)
Err.Clear
End If
Next i
'removing filter in master sheet
shData.Range("a1").AutoFilter
End Sub
When I run above macro I don't know why it is giving Type Mismatch Error after MsgBox coll(j) , simply I want to store data in Array and I'm passing that data , Here I am using For Each r In Range("A1:A10") Where A10 length is static how can I find last written column?
When you add something to collection the key needs to be a string so use:
coll.Add r.Value, CStr(r.Value)
instead of:
coll.Add r.Value, r.Value
You are still assigning coll(j) to a Variant which is not an array.
You need to:
ReDim myArr(1 to coll.Count)
Before your for loop and then in the loop:
myArr(j) = coll(j)
Before attempting to respond to this question, I would like to write what I believe you are trying to accomplish; when you confirm this is what you are trying to do, I will try to help you get working code to achieve it. This would normally be done with comments, but the threads of comments so far are a bit disjointed, and the code is quite complex...
You have data in a sheet (called "sheet1" - it might be something else though)
The first column contains certain values that might be repeated
You don't know how many columns there might be... you would like to know that though
You attempt to find each unique value in column A (call it the "key value"), and display it (one at a time) in a message box. This looks more like a debug step than actual functionality for the final program.
You then turn on the autofilter on column A; selecting only rows that match a certain value
Using that same value as the name of a sheet, you see if such a sheet exists: if it does, you clear its contents; if it does not, then you create it at the end of the workbook (and give it the name of the key)
You select all rows with the same (key) value in column A on sheet1, and copy them to the sheet whose name is equal to the value in column A that you filtered on
You want to repeat step 5-8 for each of the unique (key) values in column A
When all is done, I believe you have (at least) one more sheet than you had key values in column A (you also have the initial data sheet); however you do not delete any "superfluous" sheets (with other names). Each sheet will have only rows of data corresponding to the current contents of sheet1 (any earlier data was deleted).
During the operation you turn autofiltering on and off; you want to end up with auto filter disabled.
Please confirm that this is indeed what you are attempting to do. If you could give an idea of the format of the values in column A, that would be helpful. I suspect that some things could be done rather more efficiently than you are currently doing them. Finally I do wonder whether the whole purpose of organizing your data in this way might be to organize the data in a specific way, and maybe do further calculations / graphs etc. There are all kinds of functions built in to excel (VBA) to make the job of data extraction easier - it's rare that this kind of data rearranging is necessary to get a particular job done. If you would care to comment on that...
The following code does all the above. Note the use for For Each, and functions / subroutines to take care of certain tasks (unique, createOrClear, and worksheetExists). This makes the top level code much easier to read and understand. Also note that the error trapping is confined to just a small section where we check if a worksheet exists - for me it ran without problems; if any errors occur, just let me know what was in the worksheet since that might affect what happens (for example, if a cell in column A contains a character not allowed in a sheet name, like /\! etc. Also note that your code was deleting "CurrentRegion". Depending on what you are trying to achieve, "UsedRange" might be better...
Option Explicit
Sub Solution()
Dim shData As Worksheet
Dim nameRange As Range
Dim r As Range, c As Range, A1c As Range, s As String
Dim uniqueNames As Variant, v As Variant
Set shData = Sheets("Sheet1") ' sheet with source data
Set A1c = shData.[A1] ' first cell of data range - referred to a lot...
Set nameRange = Range(A1c, A1c.End(xlDown)) ' find all the contiguous cells in the range
' find the unique values: using custom function
' omit second parameter to suppress dialog
uniqueNames = unique(nameRange, True)
Application.ScreenUpdating = False ' no need for flashing screen...
' check if sheet with each name exists, or create it:
createOrClear uniqueNames
' filter on each value in turn, and copy to corresponding sheet:
For Each v In uniqueNames
A1c.AutoFilter Field:=1, Criteria1:=v, _
Operator:=xlAnd
A1c.CurrentRegion.Copy Sheets(v).[A1]
Next v
' turn auto filter off
A1c.AutoFilter
' and screen updating on
Application.ScreenUpdating = True
End Sub
Function unique(r As Range, Optional show)
' return a variant array containing unique values in range
' optionally present dialog with values found
' inspired by http://stackoverflow.com/questions/3017852/vba-get-unique-values-from-array
Dim d As Object
Dim c As Range
Dim s As String
Dim v As Variant
If IsMissing(show) Then show = False
Set d = CreateObject("Scripting.Dictionary")
' dictionary object will create unique keys
' have to make it case-insensitive
' as sheet names and autofilter are case insensitive
For Each c In r
d(LCase("" & c.Value)) = c.Value
Next c
' the Keys() contain unique values:
unique = d.Keys()
' optionally, show results:
If show Then
' for debug, show the list of unique elements:
s = ""
For Each v In d.Keys
s = s & vbNewLine & v
Next v
MsgBox "unique elements: " & s
End If
End Function
Sub createOrClear(names)
Dim n As Variant
Dim s As String
Dim NewSheet As Worksheet
' loop through list: add new sheets, or delete content
For Each n In names
s = "" & n ' convert to string
If worksheetExists(s) Then
Sheets(s).[A1].CurrentRegion.Clear ' UsedRange might be better...?
Else
With ActiveWorkbook.Sheets
Set NewSheet = .Add(after:=Sheets(.Count))
NewSheet.Name = s
End With
End If
Next n
End Sub
Function worksheetExists(wsName)
' adapted from http://www.mrexcel.com/forum/excel-questions/3228-visual-basic-applications-check-if-worksheet-exists.html
worksheetExists = False
On Error Resume Next
worksheetExists = (Sheets(wsName).Name <> "")
On Error GoTo 0
End Function

Setting one cell's address equal to another...... In another workbook

Say I had just copied a table to another workbook, and wanted to write a macro to verify the copy was successful, I would have to have two range variables to compare them. How do I assign the address of the cell being compared to follow the other cell's address in the other workbook, so that they are both offset the same way, even if the two starting cells are different, and so can still be compared correctly?
Change the originalRange to your original data range in the desired workbook. If this is run immediately after your paste you can use Selection for the pastedValue data. If not then change this accordingly:
Dim i As Double, j As Double
Dim originalRange() As Variant, pastedValue() As Variant
Dim copyPassed As Boolean
originalRange = Workbooks("Book1").Sheets("Sheet1").Range("A1").CurrentRegion.Value
pastedValue = Selection
copyPassed = True
For i = 1 To UBound(pastedValue, 1)
For j = 1 To UBound(pastedValue, 2)
If (originalRange(i, j) <> pastedValue(i, j)) Then
copyPassed = False
GoTo test_exit:
End If
Next j
Next i
test_exit:
MsgBox "Copy Passed: " & copyPassed