How to write the contents of a dictionary to a MessageBox - vb.net

In VB.NET I want to write the contents of a dictionary to a message box.
The dictionary is rather basic
Dim x As New Dictionary(Of Integer, Users)
x.Add("1", New Users("1", "Simon"))
The user class contains 2 attributes, user ID (Integer) and Username (String).
I am struggling to write the dictionary contents. I would like to write each dictionary entry to a string but i am having no success as I keep getting the error message:
Argument 'Prompt' cannot be converted to type 'String'.

You are passing a string where you specified an integer:
Fix:
Dim x As New Dictionary(Of Integer, Users)
x.Add(1, New Users(1, "Simon"))
Then to show the contents:
Dim sb As New StringBuilder
For Each item As KeyValuePair(Of Integer, Users) In x
sb.AppendLine(item.Key & ") " & item.Value.ToString)
Next
MessageBox.Show(sb.ToString())
Your Users class would need to override the ToString function or change the ToString call to the property in Users that shows the user's name.
Update to Users class:
Public Class Users
Private _p1 As Integer
Private _p2 As String
Sub New(ByVal p1 As Integer, ByVal p2 As String)
_p1 = p1
_p2 = p2
End Sub
Public Overrides Function ToString() As String
Return _p2
End Function
End Class

Here you go.
Dim sbMessage As New System.Text.StringBuilder(500)
For Each wKey As Integer In x.Keys
sbMessage.Append("Key = ").Append(wKey).Append(", Value = ").Append(x.Item(wKey).ToString()).AppendLine()
Next
MessageBox.Show(sbMessage.ToString)
To make this useful, you will need to override the ToString method in the Users class. For example, assuming that there is an ID and a name in this class:
Public Overrides Function ToString() As String
Dim sbText As New System.Text.StringBuilder(500)
sbText.Append("ID = ").Append(Me.Id).Append(", Name = ").Append(Me.Name)
Return sbText.ToString
End Function

For Each kvp As KeyValuePair(Of Integer, Users) In x
Console.WriteLine("Key = {0}, Value = {1}", _
kvp.Key, kvp.Value)
Next kvp
Something like that, bearing in mind that your Value will be a Users object, and that as #LarsTech said, you should pass in an integer instead of a string into the Dictionary

Related

Split() doesn't work properly

well I'm doing a computing assessment and well I've ran into an issue with splitting a string. For some reason when the string splits the array stores the whole thing in Variable(0). The error that occurs is when it tries to assign TicketID(Index) a value, it says that the array is out of bound.
Here's the code:
Private Sub ReadInformation(ByRef TicketID() As String, CustomerID() As String, PurchaseMethod() As Char, NumberOfTickets() As Integer, FileName As String)
Dim Line, TextArray(3) As String
Dim Index As Integer
FileOpen(1, FileName, OpenMode.Input)
For Index = 0 To 499
Input(1, Line)
TextArray = Line.Split(",")
CustomerID(Index) = TextArray(0)
TicketID(Index) = TextArray(1)
NumberOfTickets(Index) = TextArray(2)
PurchaseMethod(Index) = TextArray(3)
MessageBox.Show(CustomerID(Index))
Next
FileClose()
End Sub
Here's the first 10 lines of the TextFile I'm trying to read:
C001,F3,10,S
C002,F3,2,O
C003,F3,3,S
C004,W2,9,S
C005,T3,10,S
C006,F3,2,S
C007,W1,3,O
C008,W3,1,O
C009,T2,2,S
C010,F2,9,O
Here's the Error Message I receive:
Error Message
I would use some Lists instead of arrays. In this way you don't have to worry about length of the arrays or if there are fewer lines than 500. Of course, using the more advanced NET Framework methods of the File.IO namespace is a must
Private Sub ReadInformation(TicketID As List(Of String), _
CustomerID As List(Of String), _
PurchaseMethod As List(Of Char), _
NumberOfTickets As List(Of Integer), _
FileName As String)
for each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
CustomerID.Add(TextArray(0))
TicketID.Add(TextArray(1))
' This line works just because you have Option Strict Off
' It should be changed as soon as possible
NumberOfTickets.Add(TextArray(2))
PurchaseMethod.Add(TextArray(3))
End If
Next
End Sub
You can call this version of your code declaring the 4 lists
Dim TicketID = New List(Of String)()
Dim CustomerID = New List(Of String)()
Dim PurchaseMethod = New List(Of Char)()
Dim NumberOfTickets = New List(Of Integer)()
ReadInformation(TicketID, CustomerID, PurchaseMethod, NumberOfTickets, FileName)
Another approach more Object Oriented is to create a class that represent a line of your data. Inside the loop you create instances of that class and add the instance to a single List
Public Class CustomerData
Public Property TicketID As String
Public Property CustomerID As String
Public Property NumberOfTickets As Integer
Public Property PurchaseMethod As Char
End Class
Now the loop becomes
Private Function ReadInformation(FileName As String) as List(Of CustomerData)
Dim custData = New List(Of CustomerData)()
For Each line in File.ReadLines(FileName)
Dim TextArray = Line.Split(","c)
if TextArray.Length > 3 Then
Dim data = new CustomerData()
data.CustomerID = TextArray(0)
data.TicketID = TextArray(1)
data.NumberOfTickets = TextArray(2)
data.PurchaseMethod = TextArray(3)
custData.Add(data)
End If
Next
return custData
End Function
This version requires the declaration of just one list
You can call this version of your code passing just the filename and receiving the result fo the function
Dim customers = ReadInformation(FileName)
For Each cust in customers
Console.WriteLine(cust.CustomerID)
...
Next
Or use it as an array
Dim theFirstCustomer = customers[0]
Console.WriteLine(theFirstCustomer.CustomerID)

How to query an arraylist using Linq that stores dictionaries

The following code reads query results from oracle data reader and stores each record in a dictionary and appends the dictionaries to an array list :
Dim dr As OracleDataReader = cmd.ExecuteReader()
'loop oracle data records and store them to dictionaries
'append dictionaries to an array list
Dim arr As New ArrayList
While dr.Read
Dim dict As New Dictionary(Of String, Object)
For count As Integer = 0 To (dr.FieldCount - 1)
dict.Add(dr.GetName(count), dr(count))
Next
arr.Add(dict)
End While
How do I write a LINQ query that can be used to retrieve values from the dictionaries stored in the array list? Please help. I've been searching and have not got any good answers
First of all, don't use ArrayList, ever. It is there for backwards compatibility but has no usage. I can make answer short - there is no use of LINQ with ArrayList. Use generic List(Of T) and LINQ to search values in it. No need for Dictionary either. This is the old style. We used Dictionary because it has key
I see, you trying to create your table structure but no need for this. First of all, there is System.Data.DataTable, which can be queried on client.
Or use this technique
Public Class User
Public Property Id As Integer
Public Property Name As String
Public Property Email As String
Public Property Country As String
End Class
Private Function LoadUsers() As List(Of User)
Dim uList As New List(Of User)()
' Some Code goes here
While dr.Read()
Dim u As New User()
u.Id = dr("Id")
u.Name = dr("Name")
u.Email = dr("Email")
u.Country = dr("Country")
uList.Add(u)
End While
. . . . . . .
Return uList
End While
' somewhere in class set member variable
_users = LoadUsers()
' And then you can search for info using LINQ
Public Function FindByCountry(ByVal country As String) As List(Of User)
Return _users.Where(Function(u) u.Country.Equals(country, StringComparison.OrdinalIgnoreCase))
End
The downside of this approach - you need Find function for each field. But what if you can pass a function itself. See- you have Name, email, Country - all strings. Here what you can do
Class Client
Sub SearchStrings(ByVal searchOption String, Byval searchValue As String)
Dim f As Func(Of User, boolean)
If searchOption = "Name" Then
f = Function(u as User)(u.Name.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
ElseIf searchOption = "Country" Then
f = Function(u as User)(u.Country.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
ElseIf searchOption = "Email" Then
f = Function(u as User)(u.Email.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
Else
. . . .
End If
dataGrd.DataSource = myRepository.FindByString(f)
End Sub
End Class
' In your repository class
public sub FindByString(ByVal f as Func(Of String, Boolean)) As List(Of User)
_users.Where(f).ToList()
End sub
' use this to search single user
public sub FindByInteger(ByVal f as Func(Of Integer, Boolean)) As User
_users.SingleOrDefault(f)
End sub
The bottom line - drop what you do and use modern and efficient techniques. And above are just couple of them

Readonly in keyvaluepair

Well I have created a program that takes some files (Mp3) and change their tags
recently I wanted to add some new Subs (like: Take the songs name and make every letter in it upercase). The problem is that i use a list with its items to be keyvaluepairs
Public MP3List As New List(Of KeyValuePair(Of String, String))
When i tried to edit the key or value of any Item in that list i get an error (That this is READONLY)
Example:
For Each Song In MP3List
Song.Key = "Something"
Next
I add items like this :
Private Function OpenAFile()
Dim MP3List1 = MP3List
Dim oFileDialog As New OpenFileDialog
oFileDialog.Title = "Επέλεξε ένα MP3 Άρχειο"
oFileDialog.Filter = "MP3 Files|*.mp3|All Files|*.*"
oFileDialog.Multiselect = True
Dim Path As String = ""
Dim Name As String = ""
Dim NewPair As New KeyValuePair(Of String, String)
If oFileDialog.ShowDialog = Windows.Forms.DialogResult.OK Then
For Each sPath In oFileDialog.FileNames
Path = New String(sPath)
Name = New String(Strings.Split(Path, "\").ToList(Strings.Split(Path, "\").ToList.Count - 1))
NewPair = New KeyValuePair(Of String, String)(Name, Path)
If Not MP3List1.Contains(NewPair) Then MP3List1.Add(NewPair)
Next
End If
Return MP3List1
End Function
So the idea is this: Each time i press A button to add a song it will run the function OpenAFile() and it was working fine then . Now that i want to change a key or value i get this error
Thanks for the Help and sorry for bad english
The Keys in a KeyValuePair are readonly because they are often used as the key in a hash table. Changing the key would cause issues where you would lose your item in the hash.
If you want to do something like this, you could always create your own data type that stores a key and value. An overly simplified example would be as follows.
Public Structure PathNamePair
Public Property Path As String
Public Property Name As String
Public Sub New(path As String, name As String)
Me.Path = path
Me.Name = name
End Sub
End Structure
I will note that in order to get better performance with your Contains method, you should also implement IEquatable(Of T), but that's probably beyond the scope of this question. I will also note that it is not best practice to have a ValueType (Structure) that is mutable.

Why does string.join return list object in VB.Net

I am having trouble understanding the difference between these two commands that in my mind should do the same thing. I have posted the entire code below in case anything is unclear.
I have created two functions in class Person, one that returns a list containing first,middle and last names and one that returns a concatenated string of the name. I reference the function that returns the list to concatenate the string with the line below:
FullName = String.Join(" ", Me.Get_NameList())
However, when I call:
Console.WriteLine(Person1.Print_Name())
I get what looks like the list object instead of the string:
System.Collections.Generic.List`1[System.String]
If I change the code to look like this:
Public Function Print_Name()
Dim FullNameList As List(Of String) = Me.Get_NameList()
Dim FullName As String
FullName = String.Join(" ", FullNameList)
Return FullName
End Function
The console prints:
John Q Doe
Why am I getting a different answer by first assigning the list to a variable and then joining it? Does this have something to do with how the list is stored in memory?
Thanks in advance for the help.
Here is the full code:
Imports System
Module Module1
Sub Main()
Dim Person1 As New Person("John", "Q", "Doe")
Console.WriteLine("Get_Name Values")
Dim g1 As List(Of String) = Person1.Get_NameList()
Console.WriteLine(String.Join(" ", g1))
Console.WriteLine("Print_Name Values")
Console.WriteLine(Person1.Print_Name())
End Sub
End Module
Class Person
Private FirstName As String
Private MiddleName As String
Private LastName As String
Public Sub New(ByVal Fn As String, ByVal Mn As String, ByVal Ln As String)
FirstName = Fn
MiddleName = Mn
LastName = Ln
End Sub
Public Function Get_NameList()
Dim NameList As New List(Of String)
NameList.Add(FirstName)
NameList.Add(MiddleName)
NameList.Add(LastName)
Return NameList
End Function
Public Function Print_Name()
'Dim FullNameList As List(Of String) = Me.Get_NameList()
Dim FullName As String
FullName = String.Join(" ", Me.Get_NameList())
Return FullName
End Function
End Class
GetNameList returns an Object (because you don't specify the return type).
So the Join method is getting an object. So the VB.Net is turning the Object into a String() with one element that is Object.ToString(). Sometimes the method, especially if it is an old school VB holdover, would check to see if the object passed was an IEnumerable and just iterate over the Objects in the passed object. But not always. So having Strict and Explicit OFF can lead to very strange and hard to find bugs. Those two things should only be OFF in very specific cases where you want all the flexibility turning them off gives you AND you are ready to deal with the oddities that result.
Change the return type of Get_NameList to List(Of String)
And turn on option Strict ON and Option Explicit On to see your other problems.
if you change this line:
Public Function Get_NameList()
to
Public Function Get_NameList() AS List(Of String)
And this line
Public Function Print_Name()
to
Public Function Print_Name() as string
it will work

How to convert a string of key/value pairs to HashTable or Dictionary or?

In VB.NET, how can I convert the following string into some kind of key/value type such as a Hashtable, Dictionary, etc?
"Name=Fred;Birthday=19-June-1906;ID=12345"
I want to extract Birthday or ID without having to split the string into an array.
EDIT: I'd prefer not to split the string into an array in case the format of the string changes later. I don't have control over the string. What if someone switches the order around or adds another element?
I’m currently unable to test this, lacking a VB compiler, but the following solution should also work, and it has the advantage of not requiring an explicit loop. It uses the Linq method ToDictionary and two nested Split operations:
Dim s = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d = s.Split(";"c).Select(Function (kvp) kvp.Split("="c)) _
.ToDictionary( _
Function (kvp) kvp(0), _
Function (kvp) kvp(1))
First, we split on the outer delimiter (i.e. the semi-colon). From the resulting array, we select by splitting again, this time on =. The resulting array of arrays is converted to a dictionary by specifying that the first item is to become the key and the second is to become the value (the identifier kvp stands for “key-value pair”).
Since I can’t check the exact VB syntax and the above may contain subtle errors, here is the equivalent C# code (tested for correctness):
var s = "Name=Fred;Birthday=19-June-1906;ID=12345";
var d = s.Split(';').Select(kvp => kvp.Split('='))
.ToDictionary(kvp => kvp[0], kvp => kvp[1]);
Not sure why you don't want to split it. If you're sure there won't be any extra = or ; then you could just do:
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim d As New Dictionary(Of String, String)
For Each temp As String In s.Split(";"c)
Dim index As Int32 = temp.IndexOf("="c)
d.Add(temp.Substring(0, index), temp.Substring(index + 1))
Next
Which might not be beautiful, but is very easy to understand.
input.Split(";"c) returns an array of key/value:
{ "Name=Fred", "Birthday=19-June-1906" , "ID=12345" }
so pair.Split("="c) returns { "Name", "Fred" } etc
If you want an alternative to doing a String.Split; there is always Regular Expressions as an alternative:
Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim match As Match = Regex.Match("Name=Fred;Birthday=19-June-1906;ID=12345", "(?<Name>[^=]*)=(?<Value>[^;]*);?")
While (match.Success)
map.Add(match.Groups("Name").Value, match.Groups("Value").Value)
match = match.NextMatch()
End While
The regular expression itself could be beefed up to better handle whitespace between key/value's and pair's but you hopefully get the idea. This should only pass through the string once to build up a string dictionary of keys and values.
Dim persSeparator as string=";"
Dim keyValSeparator as string="=";
Dim allPersons As New Dictionary(Of String, Person)
Dim str As String = "Name=Fred;Birthday=19-June-1906;ID=12345"
Dim parts As New List(Of String)(str.Split(persSeparator.ToCharArray)) 'why dont want you to split this string??
Dim person As New Person
For Each part As String In parts
Dim keyValue() As String = part.Split(keyValSeparator.toCharArray())
Select Case keyValue(0).ToUpper
Case "ID"
person.ID = keyValue(1)
Case "NAME"
person.Name = keyValue(1)
Case "BIRTHDAY"
person.BirthDay= keyValue(1)
End Select
Next
If Not allPersons.ContainsKey(person.ID) Then
allPersons.Add(person.ID, person)
End If
Public Class Person
Private _name As String
Private _birthday As String
Private _id As String = String.Empty
Public Sub New()
End Sub
Public Sub New(ByVal id As String)
Me._id = id
End Sub
Public Sub New(ByVal id As String, ByVal name As String)
Me._id = id
Me._name = name
End Sub
Public Sub New(ByVal id As String, ByVal name As String, ByVal birthday As String)
Me._id = id
Me._name = name
Me._birthday = birthday
End Sub
Public Property ID() As String
Get
Return Me._id
End Get
Set(ByVal value As String)
Me._id = value
End Set
End Property
Public Property Name() As String
Get
Return Me._name
End Get
Set(ByVal value As String)
Me._name = value
End Set
End Property
Public Property BirthDay() As String
Get
Return Me._birthday
End Get
Set(ByVal value As String)
Me._birthday = value
End Set
End Property
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If TypeOf obj Is Person AndAlso Not obj Is Nothing Then
Return String.Compare(Me._id, DirectCast(obj, Person).ID) = 0
Else : Return False
End If
End Function
End Class
If you were just wanting to extract the birthday and ID from the string and place as a value pair in some sort of dictionary, for simplicity I would use regular expressions and then a generic dictionary (of string, valuepair structure). Something like this:
Imports System.Text.RegularExpressions
Imports System.Collections.Generic
Sub Main()
Dim Person As New Dictionary(Of String, ValuePair)
Dim s As String = "Name=Fred;Birthday=19-June-1906;ID=12"
Dim r As Regex = New Regex("Name=(.*);Birthday=(.*);ID=(.*$)")
Dim m As Match = r.Match(s)
Person.Add(CStr(m.Groups(1).Value), _
New ValuePair(CDate(m.Groups(2).Value), CInt(m.Groups(3).Value)))
Console.WriteLine(Person("Fred").Birthday.ToString)
Console.WriteLine(Person("Fred").ID.ToString)
Console.Read()
End Sub
Friend Structure ValuePair
Private _birthday As Date
Private _ID As Int32
Public ReadOnly Property ID() As Int32
Get
Return _ID
End Get
End Property
Public ReadOnly Property Birthday() As Date
Get
Return _birthday
End Get
End Property
Sub New(ByVal Birthday As Date, ByVal ID As Int32)
_birthday = Birthday
_ID = ID
End Sub
End Structure