Get data from a collection - vb.net

I want to make a collection to have data available
Example:
Dim b As New Collection
colb = New Collection
b.Add("system", "1001", "SYSTEM")
b.Add("network", "1002", "NETWORKA")
b.Add("networksecond", "1010", "NETWORKB")
colb.Add(b, "list")
im looking for a function to get data from this collection:
I want to, based on the ID (Second number) get the first and third value
So if I search for 1010, I need to have the value Network and NETWORKA

VB6 called, they want their Collection back.
No, seriously, please consider using a Dictionary instead of the old, legacy Collection class. Behold the beauty of generics and strong typing:
Dim dic As New Dictionary(Of Integer, Tuple(Of String, String))
dic.Add(1001, Tuple.Create("system", "SYSTEM"))
dic.Add(1002, Tuple.Create("network", "NETWORKA"))
dic.Add(1010, Tuple.Create("networksecond", "NETWORKB"))
' Search
Dim t As Tuple(Of String, String) = Nothing
If dic.TryGetValue(1002, t) Then
Console.WriteLine(t.Item1) ' prints "network"
Console.WriteLine(t.Item2) ' prints "NETWORKA"
End If
As soon as you have more than two values, I suggest that you use a specialized class instead of a Tuple to increase readability.

Also, you can simply use List(Of T). In most cases this is enough. Dictionary is good for fast search out long list by a single key.
'declare model
Public Class NetworkModel
Public Property Id As Integer
Public Property Name1 As String
Public Property Name2 As String
End Class
' load list of models
Private _modelList As New List(Of NetworkModel)()
.......
' search using LINQ
Dim model As NetworkModel = _modelList.FirstOrDefault(Function(m) m.Id = 1001)
If model IsNot Nothing Then . . . . .

Related

Convert an unknown structure to an untyped Object in VB.NET

I'd like to convert an unknown basic structure to an Object (no type here).
I'm building a library that will be used by many users to extract data from my system but don't want to do a new function for everyone of them. They have to know what will be the result.
In vb, it is possible to create an Object with some properties and use it as it is a regular Class like so:
Dim myObj as New With { .name = "Matt", .age = "28" }
MsgBox( myObj.name & " is now " & myObj.age & " years old.")
So far, so good.
Next step : my user will give me some instructions that I need to extract data from various DBs, and I've no idea of what the result will be.
What I know after the execution is a list of String containing the columns of the result set and, of course a (set of) rows.
And here is the problem of course
My function (for a single row) so far:
Public Function GetData(ByVal instructions as String) as Object ' User is supposed to know what will be inside, instructions is as XML describing DB, table, query, ...
' Do what is needed to retrieve data
' Here I have a variable cols As List(Of String) ' e.g. ("BP", "NAME", "VAT")
Dim o As New With ???
Return o
End Function
What I've tried: build a fake JSon on the fly, and try to Deserialize to Object.
But even if it seems to work, I (and the user) can't access the property as in my top piece of code like:
MsgBox(o.BP)
I know that I could do
Public Function GetData(Of T As {New})(ByVal instructions as String) As T
Dim o As T
' Use some Reflexion to TryInvokeMember of T
Return o
End Function
But I wanted to remove the hassle to create a class to use my code.
Plus, My librairy will be use in a webservice and the class of the user is then unknown.
One approach could be - to use Dictionary(Of String, Object)
Public Function GetData(instructions as String) As Dictionary(Of String, Object)
Dim data = ' Load data
Dim columns As String() = { "BP", "NAME", "VAT" }
Return columns.ToDictionary(
Function(column) column,
Function(column) data.GetByColumnName(column)
)
End Function
` Usage
Dim result = GetDate("instructions: ['BP', 'NAME']")
' Because user knows it is Integer
Dim bpValue = DirectCast(result.Item("BP"), Integer)
Thanks to #GSerg, #Fabio and a few other searches about ExpandoObject, I did it !
Imports System.Dynamic
Dim o As Object = New ExpandoObject()
For Each col In cols
DirectCast(o, IDictionary(Of String, Object)).Add(col, row.GetString(col))
Next

extract list of string from list of custom class

i have a list(of custom class)
and i want to extract a list of all 'name' String, from it, through linq
I know how to do with a loop, but i need to get it with a linear, brief linq instruction.
i've checked this help
C# Extract list of fields from list of class
but i have problem in linq correct syntax
in particular because i would like to extract a New List(Of String)
Class Student
Sub New(ByVal NewName As String, ByVal NewAge As Integer)
Name = NewName
Age = NewAge
End Sub
Public Name As String
Public Age As Integer
End Class
Public Sub Main
Dim ClassRoom as New List(Of Student) From {New Student("Foo",33), New Student("Foo2",33), New Student("Foo3",22)}
Dim OneStudent as Student = ClassRoom(0)
Dim AllStudentsNames As New List(Of String) From {ClassRoom.Select(Function(x) x.Name <> OneStudent.Name).ToList}
End Sub
But something wrong...
Any help?
P.S. Since c# it's close to vb.Net, also c# helps are well welcome.
First, you don't need to create a new list From the one returned by the LINQ method. It's already in a new list at that point, so you can just set AllStudentsNames equal directly to what the ToList method returns.
Second, you are not selecting the name. You are selecting the result of the equality test to see if the names are different. In other words, when you say Select(Function(x) x.Name <> OneStudent.Name), that returns a list of booleans, where they true if the names are different and false if the names are the same. That's not what you want. You want the list of names, so you need to select the name.
Third, if you need to filter the list so that it only returns ones where the name is different, then you need to add a call to the Where method.
Dim AllStudentsNames As List(Of String) = ClassRoom.
Where(Function(x) x.Name <> OneStudent.Name).
Select(Function(x) x.Name).
ToList()

How to use reflection to get keys from Microsoft.VisualBasic.Collection

I know that it is not possible to get keys (How to get the Key and Value from a Collection VB.Net) and it is better to use other classes. However I am debugging 10 years old code and I cannot change it.
In the Watch Window I see:
So it seems to be possible to deal the collection as "List(of KeyValuePair)". Can I do it in code or is it only an internal translation.
I basically need to list all KEYs in a Collection.
VisualBasic.Collection has an undocumented private method InternalItemsList which allows reading keys besides values. The type of InternalItemsList is Microsoft.VisualBasic.Collection.FastList which does not seem to be Enumerable but has a method Item which returns items with a zero based index. The type of those items is Microsoft.VisualBasic.Collection.Node. An item has two private properties m_Value and m_Key. And here we are.
Following code illustrates how to convert VisualBasic.Collection to List(Of KeyValuePair.
Dim flg As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
Dim InternalList As Object = col.GetType.GetMethod("InternalItemsList", flg).Invoke(col, Nothing)
Dim res As New List(Of KeyValuePair(Of String, Object))
For qq As Integer = 0 To col.Count - 1
Dim Item As Object = InternalList.GetType.GetProperty("Item", flg).GetValue(InternalList, {qq})
Dim Key As String = Item.GetType.GetField("m_Key", flg).GetValue(Item)
Dim Value As Object = Item.GetType.GetField("m_Value", flg).GetValue(Item)
res.Add(New KeyValuePair(Of String, Object)(Key, Value))
Next
I know that using undocumented functions is not safe but it is for internal use only and only solution I have managed to find.

.NET: Quick way of using a List(Of String) as the Values for a new Key/Value collection?

I have a List(Of String) object and need to create a new collection based on the strings' values. The new collection will be of a custom class with two string fields - call them Key and Value (but it's not the built-in KeyValue class, it's a custom class).
All the values of Key will be the same, it's just Value that I want to source from the string list. Example:
Dim slValues = New List(Of String)({"Cod", "Halibut", "Herring"})
Dim myList = New List(Of myClass)( ... amazing initialisation line here? )
Class myClass
Public Key As String ' This will always be "Fish"
Public Value As String ' This will be the fish name.
End Class
(Note I don't actually have access to the class I'm using, so can't just change it to Public Key As String = "Fish" as a default. Key must be set at runtime.)
I can of course just do a loop and do it manually, but I'm wondering if there's a whiz-bang way to achieve this as part of the initialisation line?
How about this
Dim myList = slValues.ConvertAll(Of myClass)( _
Function(s) New YourClass With {.Key = "Fish", .Value = s})
Here's an alternative to Jodrell's answer using Linq's Select function:
Dim myList = slValues.Select(Function(fish) New CustomClass With {.Key = "Fish", .Value=fish}).ToList()
from x in Enumerable.Range(1, slValues.count-1).ToArray
select new KeyValuePair(of string,myClass) with {.Key=slValues(x),.Value=myList(x)}
hope it helps.

How to get the Key and Value from a Collection VB.Net

How do you get the key value from a vb.net collection when iterating through it?
Dim sta As New Collection
sta.Add("New York", "NY")
sta.Add("Michigan", "MI")
sta.Add("New Jersey", "NJ")
sta.Add("Massachusetts", "MA")
For i As Integer = 1 To sta.Count
Debug.Print(sta(i)) 'Get value
Debug.Print(sta(i).key) 'Get key ?
Next
Pretty sure you can't from a straight Microsoft.VisualBasic.Collection.
For your example code above, consider using a System.Collections.Specialized.StringDictionary. If you do, be aware that the Add method has the parameters reversed from the VB collection - key first, then value.
Dim sta As New System.Collections.Specialized.StringDictionary
sta.Add("NY", "New York")
'...
For Each itemKey in sta.Keys
Debug.Print(sta.Item(itemKey)) 'value
Debug.Print(itemKey) 'key
Next
I don't recommend using the Collection class, as that is in the VB compatibility library to make migrating VB6 programs easier. Replace it with one of the many classes in the System.Collections or System.Collections.Generic namespace.
It is possible to get a key with using Reflection.
Private Function GetKey(Col As Collection, Index As Integer)
Dim flg As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
Dim InternalList As Object = Col.GetType.GetMethod("InternalItemsList", flg).Invoke(Col, Nothing)
Dim Item As Object = InternalList.GetType.GetProperty("Item", flg).GetValue(InternalList, {Index - 1})
Dim Key As String = Item.GetType.GetField("m_Key", flg).GetValue(Item)
Return Key
End Function
Not using VB.Collection is recommended but sometimes we are dealing with code when it was used in past. Be aware that using undocumented private methods is not safe but where is no other solution it is justifiable.
More deailed information can be found in SO: How to use reflection to get keys from Microsoft.VisualBasic.Collection
Yes, it may well, but I want recomend that you use another Collection.
How to do you do with Reflection, the type Microsoft.VisualBasic.Collection contains some private fields, the field one should use in this case is "m_KeyedNodesHash" the field, and the field type is System.Collections.Generic.Dictionary(Of String, Microsoft.VisualBasic.Collection.Node), and it contains a property called "Keys", where the return type is System.Collections.Generic.Dictionary(Of String, Microsoft.VisualBasic.Collection.Node).KeyCollection, and the only way to get a certain key is to convert it to type IEnumerable(Of String), and the call ElementAt the function.
Private Function GetKey(ByVal col As Collection, ByVal index As Integer)
Dim listfield As FieldInfo = GetType(Collection).GetField("m_KeyedNodesHash", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim list As Object = listfield.GetValue(col)
Dim keylist As IEnumerable(Of String) = list.Keys
Dim key As String = keylist.ElementAt(index)
Return key
End Function