I have a class called ReDictionary
Edit: My class declaration
Public Class ReDictionary(Of TKey, TValue)
Inherits Dictionary(Of TKey, TValue)
which provides minor enhancements for my purposes of a dictionary with things like this.
' Wraps TryGetValue
Public Function ValueOr(key, OnNoValue)
dim TossBool = Me.TryGetValue(key, TossObj)
If TossBool Then
Return TossObj
Else
Return OnNoValue
End If
End Function
I'm trying to write a .Clone() method (shallow) as part of the class.
I've tried a function that uses a TopLevelDictionary.clone(src,dest) syntax and a sub that uses an TopLevelDict("Entry").Clone(src) syntax
Public Sub Clone(srcKey As TKey, destKey As TValue) As ReDictionary(Of object, object)
Dim newbook As New ReDictionary(Of Object, Object)
For Each kvp As KeyValuePair(Of Object, Object) In Me(srcKey)
newbook(kvp.Key) = kvp.Value.ToString()
Next
Me(destKey) = newbook
End Sub
This is almost a quasi-code. I've tried probably every combination of TKey/TValue, Object/Object, even down the the specific of String/String which the two dictionaries I want to clone happen to be. I've also tried not specifying argument/return type
The errors I typically get are Cannot Convert ReDictionary(...) to ReDictionary(...) such as when the when the functions temporary ReDictionary is (Object, Object) and the source ReDictionary is (String, String).
I'd like to avoid making my public dictionary (Object, Object) because it seems wasteful on memory.
My two dictionaries are populated within the same function on form load. One is a public and the other is local to the function but passes values (a description selected by language) global dictionary.
I've realized I could probably easily write a clone function (not part of ReDictionary that used a syntax like Dict(NewEntry) = CloneEntry(Dict("Entry"). I had something like this before when I was just trying to clone object in the Public Dictionary.
Private Function CloneObj(key) As ReDictionary(Of String, String)
Dim inObj = New ReDictionary(Of String, String)
For Each kvp As KeyValuePair(Of String, String) In pLib(key)
inObj(kvp.Key) = pLib(key)(kvp.Key)
Next
Return inObj
End Function
Although I might possibly run conflicts when I try to relax the object type.
The Global dictionary is a Dictionary(String) of Dictionaries(String) of Strings,
The local dictionary is a Dictionary(String) of cTranslations(class) with String properties.
At that point, two functions could do it, but I'm just more determined, if it's possible.
Update: This is the version I'm currently trying to use
Public Class ReDictionary(Of TKey, TValue)
Inherits Dictionary(Of TKey, TValue)
Public Function Clone(srcKey As Object) As ReDictionary(Of TKey, TValue)
Dim inObj As New ReDictionary(Of TKey, TValue)
For Each kvp As KeyValuePair(Of TKey, TValue) In Me
inObj(kvp.Key) = kvp.Value
Next
Return inObj
End Function
End Class
This is an example usage of that syntax.
Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films.Clone("Edge of Tomorrow")
Films("Birdman") = New ReDictionary(Of String, String)
Films("Birdman")("Description") = "Michael Keyton, who played a superhero 20 years ago, plays an actor who also did"
Films("The Unexpected Virtue of Ignorance") = Films.Clone("Birdman")
I actually change some properties of some of the cloned items, which is why I need to clone rather than just one entry being a reference to a sibling entry.
The function itself is actually not causing any errors, but when I try to use it I get (copied straight from debugger, only indented so that it's easy to read):
The error is pretty obvious
Value of type 'ReDictionary(Of String, ReDictionary(Of String, String))'
cannot be converted to 'ReDictionary(Of String, String)
If I change In Me to In Me(srckey), I get
Expression of type TValue which is not a collection type.
Changing the typing of srcKey to TValue or TKey also results in errors, as does removing the typing of srckey.
First things first, I want to point out that you should probably make objects instead of using dictionaries, but I digress.
So, why isn't it working.
First, lets look at the Clone function
Public Class ReDictionary(Of TKey, TValue)
Inherits Dictionary(Of TKey, TValue)
Public Function Clone(srcKey As Object) As ReDictionary(Of TKey, TValue)
Dim inObj As New ReDictionary(Of TKey, TValue)
For Each kvp As KeyValuePair(Of TKey, TValue) In Me
inObj(kvp.Key) = kvp.Value
Next
Return inObj
End Function
End Class
Notice how srcKey is never used. So this function is returning a clone of the ReDictionary you are calling it on, not the ReDictionary contained in TopLevel(srcKey)
Next, lets look at how you are using it.
Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films.Clone("Edge of Tomorrow")
As stated above, Films.Clone will return a ReDictionary of the same type of the calling ReDictionary, Meaning you are trying to assign a ReDictionary(Of String, ReDictionary(Of String, String) to Films("Live, Die, Repeat"), which is of type ReDictionary(Of String, String)
These two are obviously different types, therefore you can't do that assignment.
So what needs to be fixed:
The Clone Method
I would recommend you remove the srcKey parameter and use this method to clone the object you are calling it on (which is what it is already doing)
The Usage
Now, because Clone will clone the dictionary you are calling it on, you can do something like this:
Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films("Edge of Tomorrow").Clone()
As Joe said in the comments, an Extension Method ended up working the best but don't see how universal typing is possible short of try/catch or if/else.
I found Dim Args as type() = SourceDict.GetType().GetGenericArguments() but as I can't use variables for object types, such as Dim inObject As New Dictionary(Of Args(0), Args(1)), it seems that repetition is necessary.
Leaving this here in case it helps anyone
Public Module DictionaryExtensions '(Of Tkey, TValue)
<Extension()>
Public Function CloneDict(ByVal SourceDict As Object,
ByVal SourceKey As String) As IDictionary
Try
Dim inObject As New Dictionary(Of String, String)
For Each kvp As KeyValuePair(Of String, String) In SourceDict(SourceKey)
inObject(kvp.Key) = SourceDict(SourceKey)(kvp.Key)
Next
Return inObject
Catch
Dim inObject As New Dictionary(Of String, Translation)
For Each kvp As KeyValuePair(Of String, Translation) In SourceDict(SourceKey)
inObject(kvp.Key) = SourceDict(SourceKey)(kvp.Key)
Next
Return inObject
End Try
End Function
<Extension()>
Public Function ValueOr(ByVal SourceObject As Object,
ByVal Key As Object,
Optional ByVal onnovalue As Boolean = False)
Dim TossBool as Boolean = SourceObject.TryGetValue(Key, TossObj)
If TossBool Then
Return TossObj
Else
Return onnovalue
End If
End Function
<Extension()>
Public Function ValueOr(ByVal SourceObject As Object,
ByVal Key As Object,
ByVal onnovalue As Object)
Dim TossBool as Boolean = SourceObject.TryGetValue(Key, TossObj)
If TossBool Then
Return TossObj
Else
Return onnovalue
End If
End Function
End Module
Related
We can do this in vb.net:
Dim d= new Dictionary(of string, string) from {{"a","valA"},{"b","valB"}}
Please how can we make the following possible for convenience:
public sub Setup(args)
Dim d= new Dictionary(of string, string) from args
end sub
Thanks.
No, you can't initialize a dictionary with collection initializer syntax from a variable or parameter. It's just syntactic sugar that the compiler eats if you assign it in the way you've shown in the first snippet.
However, you can pass an IDictionary(Of String, String) and use that for the constructor of the dictionary:
Public sub Setup(dict As IDictionary(Of String, String))
Dim d = new Dictionary(Of String, String)( dict )
End Sub
Or you can pass an IEnumerable(Of KeyValuePair(Of String, String)) and use that for args.ToDictionary(Function(kv) kv):
Public sub Setup(keyVals As IEnumerable(Of KeyValuePair(Of String, String)))
Dim d = args.ToDictionary(Function(kv) kv)
End Sub
The former approach is more efficient, the latter is more general. Allowing IDictionary(Of TKey, TValue) has the advantage that you can pass more types since there are many classes that implement that interface.
I'm trying to make a Property for a List(Of KeyValuePair(Of String, sVolumeInfo)) variable. So far I've got:
Public ReadOnly Property RemoveableDrives As List(Of KeyValuePair(Of String, sVolumeInfo))
Get
RemoveableDrives = mRemoveableDrives
End Get
End Property
Private mRemovableDrives As List(Of KeyValuePair(Of String, sVolumeInfo))
Public Sub New()
mRemoveableDrives = New List(Of KeyValuePair(Of String, sVolumeInfo))
End Sub
My problem is, is that the = mRemoveableDrives does not compile, VB.NET insists on changing mRemoveableDrives to the Class name.
How can I get this to work?
Don’t use assignment to the function name, use Return.
Get
Return mRemoveableDrives
End Get
That said, it’s almost certainly a bad idea to expose a collection member nilly-willy to the outside for read and write access.
I have an extension method that works great in converting an object to my form object.
'Copy an object to a form object
<Extension()> _
Public Function FromModel(ToObject As BaseFormObject, ByRef FromObject As Object) As Boolean
ToObject = FromObject
Return True
End Function
I want to do the same thing for a list of objects.
'Copy a list of objects to form objects
<Extension()> _
Public Function FromModelList(ToList As List(Of BaseFormObject), ByRef FromList As List(Of Object)) As Boolean
For Each FromItem As Object In FromList
'Create a new BaseFormObject for every item in FromList, Add it to our From List
Dim newFormObject = New BaseFormObject()
newFormObject.FromModel(FromItem)
ToList.Add(newFormObject)
Next
Return True
End Function
Creating a new List(Of BaseFormObject) does not give me access to the method "FromModelList." What is the proper way to go about doing this?
Your code works fine.
I created this code:
Dim lst As New List(Of BaseFormObject)
I got the extension member when I typed in lst.
I'm going to go out on a limb here and suggest that you're not actually instantiating a List(Of BaseFormObject), but instead something like List(Of ActualFormObject) where ActualFormObject inherits from BaseFormObject.
So if I use this code:
Dim lst As New List(Of ActualFormObject)
Then you don't get the extension member. If that's what you're hoping for, then it's easy to fix.
Change your code to this:
<Extension()> _
Public Function FromModelList(Of T As {New, BaseFormObject})(ToList As List(Of T), ByRef FromList As List(Of Object)) As Boolean
For Each FromItem As Object In FromList
'Create a new BaseFormObject for every item in FromList, Add it to our From List
Dim newFormObject = New T()
newFormObject.FromModel(FromItem)
ToList.Add(newFormObject)
Next
Return True
End Function
Then you get the extension member on lists of sub-classes.
Say I have two dictionaries:
Dim dict1 as new dictionary(Of Integer, String)
Dim dict2 as new dictioanry(Of customType, customtype2)
I want to convert them to a list in a method and return a list of the argued dictionary value type. So....
Public Function DictToListConverter(ByVal argDict as Dictionary(Of Object, Object), ByVal argType as Type) **What goes here.
I know I can just cast the return on the calling routine, but that is not the best solution. I would not like to return a custom class which contains the values.In other words I'm looking for a way to do the following:
Pass in dictionary(Of Integer, String) and get a return of List(Of String)
at the same time, if I pass in a dictionary (of String, Boolean) then the function should return List(of Boolean)
It is looking like this is not possible and I either have to use class / struct, or just cast the object in the calling routine. Just wanted to get a verification on whether or not this request is possible.
Again, thanks!
Assuming you're using .Net 3.5 or later you can do that with the following...
Public Function DictToListConverter(Of TKey, T)(dict As Dictionary(Of TKey, T)) As List(Of T)
Return dict.Select(Function(i As KeyValuePair(Of TKey, T)) i.Value).ToList()
End Function
You need to use List(Of Object), or ArrayList, which is more or less the same.
Given the following vb.net class:
Friend Class PairCollection(Of TKey, TValue)
Inherits List(Of KeyValuePair(Of TKey, TValue))
Public Overloads Sub Add(ByVal key As TKey, ByVal value As TValue)
Me.Add(New KeyValuePair(Of TKey, TValue)(key, value))
End Sub
Public Function FindByValue(ByVal value As TValue) As List(Of KeyValuePair(Of TKey, TValue))
Return Me.FindAll(Function(item As KeyValuePair(Of TKey, TValue)) (item.Value.Equals(value)))
End Function
End Class
The function FindByValue returns a single KeyValuePair that fits a value. However, an implementation of this PairCollection might have m:1 key to value, so I wish to return all keys (and key's only) that have that value (there could be more than one).
Problem isn't made easier that I'm new to Lambda expressions in vb.net, and more familiar with C#. I could write a simple routine to iterate over the collection, but I feel there is a lambda & generic combined approach.
I think what I am trying to do is something along the lines of the following:
Public Function FindByValue2(ByVal value As TValue) As List(Of TKey)
Return Me.FindAll(Function(item As list(of TKey) (item.Value.Equals(value)))
End Function
Related reasoning behind what I am attempting is here.
You are doing it correctly. You just need to project the output with Select.
Public Function FindByValue(ByVal value As TValue) As List(Of TKey)
Return Me.Where(Function(item) item.Value.Equals(value)) _
.Select(Function(x) x.Key).ToList()
End Function
By the way, inheriting List is almost always a wrong thing to do. Try redesigning your class.