VBA error 457 key when adding to dictionary - excel

I have an array of combinations called "Keys". I want to check if each combination exists in the column I, and if so, split the existing combination in 2 strings and add them as a pair in my dictionary "combiExist".
My code throws a 457 error
Dim combiExist As Object
Set combiExist = CreateObject("Scripting.Dictionary")
For Each cle In keys
'If combination exists in my range
If Not .Range("I:I").Find(cle) Is Nothing Then
'Split string from 7th position, left part is the key, right part is the value
combiExist.Add Left(cle, 7), Right(cle, 7)
End If
Next
How can I solve this ?

Error 457 says that the key is already associated with an element of the collection. So, before assigning it to the dictionary make sure it is not there, with .Exists.
This one works rather ok, at the end it prints the dictionary:
Sub Main()
Dim combiExist As Object
Set combiExist = CreateObject("Scripting.Dictionary")
Dim combinations As Variant
combinations = Array("joeC12345678910", "C12345678910", "foooooo123")
Dim cle As Variant
For Each cle In combinations
If Not Worksheets(1).Range("I:I").Find(cle) Is Nothing Then
If combiExist.exists(Left(cle, 7)) Then
Debug.Print "Do nothing this one " & (Left(cle, 7)) & " exists!"
Else
combiExist.Add Left(cle, 7), Right(cle, 7)
End If
End If
Next
PrintDictionary combiExist
End Sub
Public Sub PrintDictionary(myDict As Object)
Dim key As Variant
For Each key In myDict.keys
Debug.Print key; "-->"; myDict(key)
Next key
End Sub
In general, do not use words like Keys for a variable name, because this one means something in VBA - usually a collection of the keys of a given dictionary. You can see the implementation of myDict.keys in PrintDictionary().

Beware of voodoo programming
Do
If Inp.AtEndOfStream = True then exit do
Line=Inp.readline
On Error Resume Next
Dict.Add(Line, "")
If err.number = 457 then err.clear
On Error Goto 0
Loop
This is the way one programs. One does and handles it.
The answers given using .exists generates needless function calls. All methods and properties are indirect function calls under the hood. That means stack setup and tear down. There will be a minimum of one function per item, if it's duplicated. But there will be two function calls for unique items.
Testing return values it is only one function per item.
Also remember every . is also a function call.
Remember COM methods/properties look like this
Err.Number = MethodName(Param1, ..., ReturnValue)
Err.Number, called an HResult in COM, returns information about your call.
Embrace errors.
What is on the stack.
The return address, the return value, any parameters (in or out), and all local variables.
In the other answer there is a minimum of 3 function calls or 4 if it does not exists.
My Code is 2 if it exists or not. That is multiplied by the number of items.

Related

How to read the filter settings from an Excel table using VBA [duplicate]

I have a Client class. Inside that class there is an array losses. First I create and populate with clients a clientsColl array. Then for each client in that array I populate its losses array.
Then I try to print into debug a first element of losses for each client. However, it doesnt work and Property let procedure not defined and property get procedure did not return an object error appears.
And the same time if I just try to display a first element of losses for the first client, without any cycle, it works fine:
Dim clientsColl() As Client
clientsColl = getClients(dataWorkbook)
Dim clientCopy As Variant
Debug.Print "first: " & clientsColl(1).getLosses(1) 'works fine
For Each clientCopy In clientsColl
Debug.Print "in for each: " & clientCopy.getLosses(1) 'error here
Next
In Client class:
Public Property Get getLosses()
getLosses = losses
End Property
Private losses() As Double
How the losses array is populated:
Public Sub calculateFinancialResult()
ReDim losses(1 To simulationCount)
ReDim profits(1 To simulationCount)
Dim i As Long
For i = 1 To simulationCount
If outcomes(i) = 1 Then
losses(i) = totalLoss
...
Else
...
End If
Next
End Sub
Why does this happen and how to fix it?
EDIT: more of the main sub:
For Each clientCopy In clientsColl
clientCopy.setSimulationCount = globals("SIMULATION_COUNT")
...
clientCopy.calculateFinancialResult
...
Next
EDIT:
At the same time a simple for cycle works fine:
Debug.Print "first: " & clientsColl(1).getLosses(1)
For tempCount = LBound(clientsColl) To UBound(clientsColl)
Debug.Print "in for each: " & _
clientsColl(tempCount).getLosses(1)
Next
To conclude what was said in comments:
Your problem (error 451) often occures when you trying to compound properties.
To represent this case we can use any structure of any object with properties.
Let's emulate it with array of collections:
Option Explicit
Sub Test()
Dim Arr As Variant
Dim Col As Collection
Dim i As Long
Dim j As Long
ReDim Arr(1 To 10)
For i = 1 To 10
Set Col = New Collection
For j = 1 To 10
Call Col.Add(j)
Next
Set Arr(i) = Col
Next
On Error Resume Next
Debug.Print Arr(1).Item(1)
Debug.Print Arr(1).Item()(1)
On Error GoTo 0
End Sub
Your problem stems from the fact that you're treating your properties as attributes. On not-so-compounded (or when your array is declared explicitly as array of class instances) level it works due to early binding. But when things start to get more complex - it's fail, since your property just another function.
Hence, to achieve what you want, you should call it explicitly with another pair of parentheses.
Your getLosses property doesn't take an argument so your syntax is actually wrong, even though VBA can cope with it when early bound. You should be using:
Debug.Print "first: " & clientsColl(1).getLosses()(1) 'works fine
For Each clientCopy In clientsColl
Debug.Print "in for each: " & clientCopy.getLosses()(1) 'error here
Next
I also meet this problem when I create my customize array class using compound properties.
I solved it by adding class statment for return value in Property Get code. Just as what #Rory said.
You could try Public Property Get getLosses() As Double in the Client class.

VBA returning a dictionary from a function

Problem: I want to create a function that gets one string and returns a dictionary.
After much reading in SO I reproduced exactly the code given in some questions and I end up with a solution that looks to work. But there are things that I dont understand.
here the code which is a bit long because includes some comments:
Private Sub testdictionary()
Dim dict_in_sub As Scripting.Dictionary
' Do I really need this??:
'Set dict_in_sub = New Scripting.Dictionary
' or this:
'Set dict_in_sub =CreateObject("Scripting.Dictionary")
Call returning_dict_func("word1.word2")
Set dict_in_sub = returning_dict_func("whatever.second")
MsgBox "in code:" & Chr(10) & dict_in_sub("A") & Chr(10) & dict_in_sub("B")
End Sub
This is the function called:
Function returning_dict_func(SC As String) As Scripting.Dictionary
' should this function return a dictionary or an object or a scrippting dictionary??
Dim dictionary_in_function
Set dictionary_in_function = CreateObject("Scripting.Dictionary")
'take the first and second part of the string separated by the period
dictionary_in_function("A") = Mid(SC, 1, InStr(SC, ".") - 1)
dictionary_in_function("B") = Mid(SC, InStr(SC, ".") + 1, Len(SC))
MsgBox dictionary_in_function("A") & Chr(10) & dictionary_in_function("B")
Set returning_dict_func = dictionary_in_function
End Function
Even if I got the code running, there are a few things that make returning a dict from a function in VBA strange
Introducing the following changes into the code make it not working:
Dim dict_in_sub as Dictionary
' it does not work. What is the difference between dictionary and scrippting.dictionary?
' defining the function as returning a dictionary does not work
Function returning_dict_func(SC As String) As Dictionary
' if both are dictionaries it does not work
Dim dict_in_sub as Dictionary
Function returning_dict_func(SC As String) As Dictionary
Why is dictionary different than scripting.dictionary?
When should you initialize with
Set dict_modifiers = CreateObject("Scripting.Dictionary")
thanks
Note:
I had a look to:
a) VBA in Excel 2010: Scripting.Dictionary return value of a function won't pass as parameter to a wrapping function call
b) Returning dictionary from function in vba. Error 450
c) Returning an dictionary object from a function in VBA
The difference between CreateObject and New is briefly you need to reference a library with the object (Microsoft Scripting Runtime) for the latter (New), and you don't need to reference anything using CreateObject.
You can also have some intellisense hints when you use the latter definition, as the type is declared and known.
You can find more in another answer: What are the differences between using the New keyword and calling CreateObject in Excel VBA?
As for the Dictionary object, Scripting.Dictionary is the right type to use. Read more about it in https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/dictionary-object.
And by the way, in VBA you have to assign variables values using Set when the value is an object (usually something different than a number or String or Date).

Is it possible to access collection which is at the bottom of stack?

The problem that I face is, I have a object/collection which will be created on the bottom of the stack and I have a program 3 levels deep, the collection will be only used at the 3(top) level, the first item will be used and removed from the collection, but I want to keep it for the entirety of the program as the next step needs to use the next item in the collection until the whole thing is done.
The best way I would like to do it is the create that collection in the bottom layer which is where the collection will be used, and keep the collection even if the collection is out of scope.
The way I am doing it right now is to create the collection at the bottom most level and pass it down the chain, cause if I create it in the top layer it will be deleted after it goes out of scope
I feel like there must be a better way solve my problem, but I just can't find it. Does anyone knows the answer?
I just set up some text in excel as follows
(A) (1)
(B) (2)
(C) (3)
(D) (4)
(E) (5)
​
'The Code works, but what I am asking is it possible to dont pass dict through all those sub
Sub Main()
Static dict As New Dictionary
Dim x As Integer
Set dict = readrange
Do While x < 3
somesub dict
x = x + 1
Loop
End Sub
'----------------------- Next Module ----------------------------------------------------
Sub somesub(dict As Dictionary) '<----------------------- Dont want this dict
'some code which doesnt not use the dict
Dictchange dict
End Sub
'----------------------- Next Module ----------------------------------------------------
Sub Dictchange(dict As Dictionary) '<----------------------- Dont want this dict too
Cells(dict(dict.Keys(0)), 4) = "Done"
'Is it possible to call dict in Main without pass the chain
'I cant use public as in the real code, "somesub" and "Dictchange" are in different module
'I could use Global, but i always feel like it just a "Dirty" way to fix thing
dict.Remove dict.Keys(0)
End Sub
'----------------------- Next Module ----------------------------------------------------
'In the real code, this is one function in a class Module
Function readrange() As Dictionary
Dim temp As New Dictionary
For i = 1 To 5
temp.Add Cells(i, 1).Value, Cells(i, 2).Value
Next i
Set readrange = temp
End Function
I hope this would help
As I already told in my comment: Make your dict a global variable.
Option Explicit
Public dict As Dictionary 'this is globally defined in a module
Sub Main()
Dim x As Long
Set dict = ReadRangeToDict
Do While x < 3
SomeProcedure
x = x + 1
Loop
End Sub
Function ReadRangeToDict() As Dictionary
Dim TempDict As New Dictionary
Dim iRow As Long
For iRow = 1 To 5
If Not TempDict.Exists(Cells(iRow, 1).Value) Then 'check if key already exists to prevent errors!
TempDict.Add Cells(iRow, 1).Value, Cells(iRow, 2).Value
End If
Next iRow
Set ReadRangeToDict = TempDict
End Function
So you can access it in any other procedure/function without giving it as a parameter.
Sub SomeProcedure()
'output the dict in the immediate window
Dim itm As Variant
For Each itm In dict
Debug.Print itm, dict(itm)
Next itm
End Sub

Generic way to check if a key is in a Collection in Excel VBA

I have different Collections in my code. Some hold Objects (of various kinds), others have types (like Long) within them.
Is there a way to check if a key is contained in the Collection that works for types as well as objects?
So far I have two functions.
First function:
Private Function ContainsObject(objCollection As Object, strName As String) As Boolean
Dim o As Object
On Error Resume Next
Set o = objCollection(strName)
ContainsObject = (Err.Number = 0)
Err.Clear
End Function
Second function:
Private Function ContainsLong(AllItems As Collection, TheKey As String) As Boolean
Dim TheValue As Long
On Error Resume Next
TheValue = AllItems.Item(TheKey)
ContainsLong = (Err.Number = 0)
Err.Clear
End Function
The reason for the two functions is that ContainsObject does not seem to work if I pass a Collection that has Longs pairs (the function always returns False.)
P.S.: The first function is a copy of the third answer from Test or check if sheet exists
You should use a Variant in the first function. You can assign an Object to a Variant, e.g. this won't error:
Sub Test()
Dim var As Variant
Dim obj As Object
Set obj = Application
var = Application
Debug.Print var
End Sub
But this will give a Type Mismatch compile error i.e. trying to assign a Long to an Object:
Sub Test()
Dim obj As Object
Dim lng As Long
lng = 3
Set obj = lng
End Sub
So, for a generic function (along the lines of your code) to check if a Collection key is valid, you can use:
Function HasKey(coll As Collection, strKey As String) As Boolean
Dim var As Variant
On Error Resume Next
var = coll(strKey)
HasKey = (Err.Number = 0)
Err.Clear
End Function
Test code:
Sub Test()
Dim coll1 As New Collection
coll1.Add Item:=Sheet1.Range("A1"), Key:="1"
coll1.Add Item:=Sheet1.Range("A2"), Key:="2"
Debug.Print HasKey(coll1, "1")
Dim coll2 As New Collection
coll2.Add Item:=1, Key:="1"
coll2.Add Item:=2, Key:="2"
Debug.Print HasKey(coll2, "1")
End Sub
There is a useful article on MSDN regarding this. The context is VB6 but relates to VBA.
Few typos as per comments have already been corrected during edit of your post.
In response to your question I would like to cover related aspects.
While Using keys in collections has mainly three advantages
- If the order changes your code will still access the correct item
- You can directly access the item without reading through the entire
collection
- It can make you code more readable.
*But at the same time there are mainly three issues with using keys in
collections
You cannot check if the key exists
You cannot change the key
You cannot retrieve the key
As per Pearsons article the Keys of a Collection are write-only -- there is no way to get a list of existing Keys of a Collection. Further going through quoted paragraph:-
Here, Coll is a Collection object in which we will store multiple
CFile objects. The CollKeys Collection is used to store the keys of
the CFile objects stored in the Coll Collection. We need this second
Collection because the Keys of a Collection are write-only -- there is
no way to get a list of existing Keys of a Collection. One of the
enhancements provided by CFiles is the ability to retrieve a list of
Keys for the Collection.
Custom Collection Classes
One way is to iterate over the members of the collection and see if there is match for what you are looking for and the other way is to catch the Item not in collection error and then set a flag to say the item does not exist. Opinions differ on these approaches whereas some people feel it is not a good method to catch error while other section feels that it will be significantly faster than iteration for any medium to large collection.
So if we go for a method to catch error then error number we get depends on exactly what caused the error. We need a code routine to check the error. In a simplest way it could be.
'c1 is the collection
For i = 1 To c1.Count
Debug.Print Err.Number, Err.Description
If Err.Number <> 0 Then Err.Clear
Next i
Error catching routines proposed by various professionals differ in the error number they consider important and include in their routine.Various commonly occurring error numbers associated with collection object are:-
Error 5 Invalid procedure call or argument.This error can also occur
if an attempt is made to call a procedure that isn't valid on the
current platform. For example, some procedures may only be valid for
Microsoft Windows, or for the Macintosh, and so on.
error 438 "object doesn't support this property or method An object
is a class instance. A class instance supports some properties
defined in that class type definition and does not support this one.
Error 457 This key is already associated with an element of this
collection.You specified a key for a collection member that already
identifies another member of the collection. Choose a different key
for this member.
Error 91 Object variable or With block variable not set.There are two
steps to creating an object variable. First you must declare the
object variable. Then you must assign a valid reference to the object
variable using the Set statement. You attempted to use an object
variable that isn't yet referencing a valid object.
Error 450 Wrong number of arguments or invalid property
assignment.The number of arguments in the call to the procedure
wasn't the same as the number of required arguments expected by the
procedure.If you tried to assign a value to a read-only property,
Among the above errors error number 438 has been considered important and the other one is 5. I am incorporating a Function routine in my sample testing program which was posted by Mark Nold 7 years back in 2008 vide SO question Determining whether an object is a member of a collection in VBA with due credit to him.
Some errors like error 457 won't be allowed at the time of program test run. I tried to populated with duplicate keys data, it gave the error at the time of program testing itself as shown in the snapshot.
After removing it is showing correct output as shown in the snap shot.
It may not be possible to get the list of keys of a collection with a vanilla collection without storing the key values in an independent array. The easiest alternative to do this is to add a reference to the Microsoft Scripting Runtime & use a more capable Dictionary instead.
I have included this approach to get the list of keys in my program.
While populating Collection it is to be ensured that the key is the second parameter and must be a unique string.
Full code of my program is.
Sub Generic_key_check()
Dim arr As Variant
Dim c1 As New Collection
Dim dic As Object
With Application
.ScreenUpdating = False
End With
Set dic = CreateObject("Scripting.Dictionary")
dic.CompareMode = vbTextCompare
'Populate the collection
c1.Add "sheet1", "sheet1"
c1.Add "sheet2", "sheet2"
c1.Add "sheet3", "sheet3"
c1.Add "sheet4", "sheet4"
c1.Add "sheet5", "sheet5"
c1.Add 2014001, "Long1"
c1.Add 2015001, "Long2"
c1.Add 2016001, "Long3"
c1.Add 2015002, "Long4"
c1.Add 2016002, "Long5"
'Populate the dictionary
dic.Add "sheet1", "sheet1"
dic.Add "sheet2", "sheet2"
dic.Add "sheet3", "sheet3"
dic.Add "sheet4", "sheet4"
dic.Add "sheet5", "sheet5"
dic.Add "Long1", 2014001
dic.Add "Long2", 2015001
dic.Add "Long3", 2016001
dic.Add "Long4", 2015002
dic.Add "Long5", 2016002
' Get a list of key items by Dictionary Method
Dim N As Variant
For Each N In dic.Keys
Debug.Print "Key: " & N, "Value: " & dic.item(N)
Next
'Test for two types of data whether key exists or not.
If InCollection(c1, "Long1") Then
'If Exists("Long1", c1) Then
Debug.Print "Good"
Else
' If there is error then print out the error number and its description.
Debug.Print Err.Number, Err.Description
Debug.Print "Not Good"
End If
If InCollection(c1, "sheet2") Then
Debug.Print "Good"
Else
Debug.Print Err.Number, Err.Description
Debug.Print "Not Good"
End If
'Checking whether desired key has populated correctly
Debug.Print c1("Sheet1")
Debug.Print c1("Long3")
'Listing out collection items to check theyexist in the collection.
For i = 1 To c1.Count
Debug.Print c1.item(i)
Next i
With Application
.ScreenUpdating = True
End With
Set c1 = Nothing
End Sub
Public Function InCollection(col As Collection, key As String) As Boolean
Dim var As Variant
Dim errNumber As Long
InCollection = False
Set var = Nothing
Err.Clear
On Error Resume Next
var = col.item(key)
errNumber = CLng(Err.Number)
On Error GoTo 0
'5 is not in, 0 and 438 represent incollection
If errNumber = 5 Then ' it is 5 if not in collection
InCollection = False
Else
InCollection = True
End If
End Function
Final output as per program as shown in the Immediate window has been shown in the Snapshot.
Apostle is almost correct with their answer. Robin's answer will not work with generic objects, but will work as written because Excel's Range object will return the cell's value. I love Apostle's use of IsObject (mostly because ths is what I had figured out as well). The code is a little over-complicated though.
If the key exists in the collection IsObject will set the variant to True or False, otherwise an error will be ignored leaving the variant empty.
Function HasKey(col As Collection, Key As String) As Boolean
Dim v As Variant
On Error Resume Next
v = IsObject(col.Item(Key))
HasKey = Not IsEmpty(v)
End Function
I want to point out that if you want to make PaulE's function a little more flexible, you can change the string parameter to a Variant, which means that you can now also use it to check either for an item key or for an item number, which is handy. Variants are a little slower if you're going to be checking a lot of collections, but for most purposes the two functions will act similarly.
Function HasItem(col As Collection, ItemKeyOrNum As Variant) As Boolean
Dim v As Variant
On Error Resume Next
v = IsObject(col.Item(ItemKeyOrNum))
HasItem = Not IsEmpty(v)
End Function
The accepted answer here is wrong (which is the case for quite a few other questions I have noticed as well, so watch out, and read all the answers). Apostle and PaulE collaborated there for the most correct answer to the specific question that was asked. I tried to use the accepted answer, but it didn't work.
The question clearly states, "Is there a way to check if a key is contained in the Collection that works for types as well as objects?"
The accepted answer DOES NOT work for objects. PaulE's answer is the final, and correct answer. I am just adding a little bit of nuance here to make the function more one-size-fits-all.
Short variant in one string:
Function keyExists(coll As Collection, key As String) As Boolean
On Error Resume Next: keyExists = IsObject(coll(key)) Or True
End Function
First, keyExists = false. Error trapper set to ignore errors. If expression (always TRUE) was calculated without errors (element with key exists), keyExists was TRUE.
Usage (with various types of values in collection):
Sub testExist()
Dim coll As New Collection
coll.Add New Collection, "1"
coll.Add Array(1, 1), "3"
coll.Add 1, "5"
coll.Add "1111", "9"
For i = 1 To 10
Debug.Print "key " & i & " is " & IIf(keyExists(coll, CStr(i)), "Exists", "Absent")
Next
End Sub
The method from Robin will fail if the Collection contains objects rather than primitive types because they need to be assigned using Set and otherwise generate an error that will result in the method returning False. Here is a small adjustment:
'Test if a key is available in a collection
Public Function HasKey(coll As Collection, strKey As String) As Boolean
On Error GoTo IsMissingError
Dim val As Variant
' val = coll(strKey)
HasKey = IsObject(coll(strKey))
HasKey = True
On Error GoTo 0
Exit Function
IsMissingError:
HasKey = False
On Error GoTo 0
End Function

Test if range exists in VBA

I have a dynamically defined named range in my excel ss that grabs data out of a table based on a start date and an end date like this
=OFFSET(Time!$A$1,IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),1,MATCH(Date_Range_End,AllDates)-IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),4)
But if the date range has no data in the table, the range doesn't exists (or something, idk). How can I write code in VBA to test if this range exists or not?
I have tried something like
If Not Range("DateRangeData") Is Nothing Then
but I get "Runtime error 1004, method 'Range' of object '_Global' failed."
Here is a function I knocked up to return whether a named range exists. It might help you out.
Function RangeExists(R As String) As Boolean
Dim Test As Range
On Error Resume Next
Set Test = ActiveSheet.Range(R)
RangeExists = Err.Number = 0
End Function
You can replicate the match in your VBA to count before using the range how many rows you would have, or you can use error handling:
On Error Resume Next
Debug.Print range("DateRangeData").Rows.Count
If Err = 1004 Then
MsgBox "Range Empty"
Exit Sub
Else
MsgBox "Range full"
End If
Err.Clear
On Error GoTo 0
This is another approach. It has the advantage to take the container and the name you want to test. That means you can test either Sheets Names or Workbook Names for example.
Like this:
If NamedRangeExists(ActiveSheet.Names, "Date") Then
...
Else
...
End If
or
If NamedRangeExists(ActiveWorkbook.Names, "Date") Then
...
Else
...
End If
Public Function NamedRangeExists(ByRef Container As Object, item As String) As Boolean
Dim obj As Object
Dim value As Variant
On Error GoTo NamedRangeExistsError:
value = Container(item)
If Not InStr(1, CStr(value), "#REF!") > 0 Then
NamedRangeExists = True
End If
Exit Function
Exit Function
NamedRangeExistsError:
NamedRangeExists = False
End Function
Depending on the application you're doing, it's good to consider using a Dictionary. They're especially useful when you wanna check whether something exists.
Take this example:
Dim dictNames as Scripting.Dictionary
Sub CheckRangeWithDictionary()
Dim nm As Name
'Initially, check whether names dictionary has already been created
If Not dictNames Is Nothing Then
'if so, dictNames is set to nothing
Set dictNames = Nothing
End If
'Set to new dictionary and set compare mode to text
Set dictNames = New Scripting.Dictionary
dictNames.CompareMode = TextCompare
'For each Named Range
For Each nm In ThisWorkbook.Names
'Check if it refers to an existing cell (bad references point to "#REF!" errors)
If Not (Strings.Right(nm.RefersTo, 5) = "#REF!") Then
'Only in that case, create a Dictionary entry
'The key will be the name of the range and the item will be the address, worksheet included
dictNames(nm.Name) = nm.RefersTo
End If
Next
'You now have a dictionary of valid named ranges that can be checked
End Sub
Within your main procedure, all you need to do is do an existence check before using the range
Sub CopyRange_MyRange()
CheckRangeWithDictionary
If dictNames.exists("MyRange") then
Sheets(1).Range("MyRange").Copy
end if
End Sub
While loading the dictionary may look a little longer, it's extremely fast to process and search. It also becomes much simpler to check whether any named range referring to a valid address exists, without using error handlers in this simple application.
Please note that when using names at sheet level rather than workbook level, it is necessary to use more elaborate keys to guarantee uniqueness. From the way the dictionary was created, if a key is repeated, the item value is overwritten. That can be avoided by using the same Exists method as a check in the key creation statement. If you need a good reference on how to use dictionaries, use this one.
Good luck!
This is an old post, but none of the rated answers has a dynamic solution to test if a name exists in a workbook or worksheet. This function below will accomplish that:
Function pg_Any_Name(thename As String) As Boolean
Dim n As Name, t As String
For Each n In ThisWorkbook.Names
t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)
If UCase(thename) = UCase(t) Then
pg_Any_Name = True
Exit Function
End If
Next n
End Function
Worth noting that this would not have worked for this specific question because OP had a dynamic defined range. This question would have been more accurately titled Test if Name is a Valid Range because the name always existed as a formula, the issue was if it was a valid RANGE. To address this question with a solution that checks both workbook and sheets... this function would work:
Function PG_Range_Name(thename As String) As Boolean
Dim n As Name, t As String
For Each n In ThisWorkbook.Names
t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)
If UCase(thename) = UCase(t) Then
On Error Resume Next
PG_Range_Name = n.RefersToRange.Columns.Count > 0
Exit Function
End If
Next n
End Function

Resources