Using dictionary within structures - vb.net

I have a structure that has a string and 2 dictionary variable. I don't know how to insert data into these dictionaries.
Public Structure librariesWithMedia
Dim strLibraryName As String
Dim dicBooksMedia As SortedDictionary(Of String, String)
Dim dicNonBooksMedia As SortedDictionary(Of String, String)
End Structure
Dim libraryMediaEntry As librariesWithMedia
This is my structure and this is how I'm storing the values.
libraryMediaEntry.dicBooksMedia.Add(key, value)
This gives me a null reference exception error. Can anyone help me understand and how I would have to take the data?

Your Structure MUST initialise/instantiate the dictionaries before you can use them.
Public Structure librariesWithMedia
'
Dim strLibraryName As String
Dim dicBooksMedia As SortedDictionary(Of String, String)
Dim dicNonBooksMedia As SortedDictionary(Of String, String)
'
Sub New(LibName As String)
strLibraryName = LibName
dicBooksMedia = New SortedDictionary(Of String, String)
dicNonBooksMedia = New SortedDictionary(Of String, String)
End Sub
'
End Structure
'
Dim libraryMediaEntry As librariesWithMedia
And then in your code, for example
Sub DoSomethingProcess()
'
libraryMediaEntry = New librariesWithMedia("Featured_Books")
libraryMediaEntry.dicBooksMedia.Add("James A Michener", "Chesapeake")
'
End Sub

Related

Create DLL file in VB.NET and use in Excel VBA

I have created a DLL file in VB.NET and I want use it in Excel VBA. When I use it like a function it is working perfect but when I use sub with a ByRef variable it does not work and Excel restarts with an error.
The code in VB.NET is:
Public Function distinctArr(ByVal arr As String()) As String()
Return arr.ToList.Distinct.ToArray
End Function
Public Sub sortArr(ByVal arr As String(), ByRef a As String())
Dim tolist As List(Of String) = arr.ToList
tolist.Sort()
a = tolist.ToArray
End Sub
This is the code in VBA:
Dim objMda As Excelcode.mda
Set objMda = New Excelcode.mda
Dim distinc_Item() As String
Dim all_Items() As String
all_Items = rng_to_string(rng_rizmetre)
distinc_Item = objMda.distinctArr(all_Items) '''This line is working perfect
Dim Sorted_Item() As String
objMda.sortArr distinc_Item, Sorted_Item
What is wrong with the code?
Finally i can find my answer.
code in vb.net
Public Class MainClass
Sub sortArr(ByVal arr As String(), ByRef sortedarr As String())
sortedarr = arr
Array.Sort(sortedarr)
End Sub
End Class
and code in excel vba:
Sub aaa()
Dim Mycode As excelcode.MainClass
Set Mycode = New excelcode.MainClass
Dim arr(2) As String
arr(0) = "m"
arr(1) = "a"
arr(2) = "d"
Dim sortedArr() As String
ReDim sortedArr(0)
Mycode.sortArr arr, sortedArr
End Sub
by this code i can pass array byval to vb.net dll then vb.net pass sorted array.

Looping through Dictionary(Of String, List(Of String))

Similar question here, but no answer : vb.net dictionary of string,dictionary...after filling readout always empty
I fill as List(Of String) with rows of text from a file and then add it to a Dictionary. The method I use to fill this Dictionary works as it should. I create:
Private dictDictionary As Dictionary(Of String, List(Of String))
loop through the text file and adding each row to a list then add that list to the dictionary with the file name as key like so:
dictDictionary.Add(sFileName, sFileRows)
sFileRows is a List(Of String) containing a MAX of 1056 elements that I need to move around based on specific options. The problem I'm having is accessing this List(Of Strings) by the Key.
I've tried:
For Each kvp As KeyValuePair(Of String, List(Of String)) In dictDictionary
Dim sKey As String = kvp.Key
Dim tempRows As List(Of String) = New List(Of String)
tempRows = dictDictionary.item(sKey)
Next
No matter what I try when I assign the List(Of String) in the Dictionary to a new List it is always empty. But the original dictionary has the rows in the List(Of String) that I read from the text file.
First Method that fills the dictionary:
Private Sub GetInfo()
Try
Dim sFileName As String = String.Empty
Dim sFileRows As List(Of String) = New List(Of String)
If IO.Directory.Exists("some directory")Then
Dim Files() As String = IO.Directory.GetFiles("directory and file type")
For Each File As String In Files
sFileName = Path.GetFileName(File)
Dim rdrRows As StreamReader = New StreamReader(File)
Dim sString As String
While rdrRows.Peek() >= 0
sString = rdrRows.ReadLine()
sFileRows.Add(sString)
End While
'Actually adding the info to the dictionary
dictDictionary.Add(sFileName, sFileRows)
rdrRows.Dispose()
sFileRows.Clear()
Next
End If
Catch ex As Exception
End Try
End Sub
Second Method to manipulate the order of elements in the List(Of String)
Private Sub ChangeStructure()
For Each kvp As KeyValuePair(Of String, List(Of String)) In dictDictionary
Dim rows As List(Of String) = kvp.Value
For Each item As String In rows
MessageBox.Show(item.ToString)
Next
Next
End Sub
There is nothing in the List(Of String) now but there was when it was filled in GetInfo()
Should be as easy as
Dim data = New Dictionary(Of String, List(Of String)) From _
{
{"Foo", New List(Of String) From {"1", "2", "3"}},
{"Bar", New List(Of String) From {"4", "5", "6"}}
}
For Each kvp in data
Console.WriteLine(kvp.Key & " says:")
For Each str in kvp.Value
Console.WriteLine(str)
Next
Next
For Each kvp As KeyValuePair(Of String, List(Of String)) In dictDictionary
Dim rows As List(Of String) = kvp.Value
If rows Is Nothing Then Continue
For Each item As String in rows
'...
Next item
Next kvp
You're are using kvp.Key, why are you not using kvp.Value?
For Each kvp As KeyValuePair(Of String, List(Of String)) In dictDictionary
Dim sKey As String = kvp.Key
Debug.Print("Filename: " & sKey)
Dim tempRows As List(Of String) = kvp.Value
Debug.Print("# of Lines: " & tempRows.Count)
Debug.Print("------------------------------")
For Each line As String In tempRows
Debug.Print(line)
Next
Debug.Print("------------------------------")
Debug.Print("")
Next
The problem is this line:
sFileRows.Clear()
List(Of String) is a reference type. After you fill the list and add it to the dictionary, you clear it, so it is empty when you try to access it later.
The solution is to create a new list each time in the loop. In other words, move this line:
Dim sFileRows As List(Of String) = New List(Of String)
inside the For loop and get rid of the call to .Clear()

Multidimensional Arrays using Dictionaries

I am trying to make a multidimensional associative array. I want it so I can have something like:
someVar(date)(hour)(category) = mssql query
I am using the following to try and prepare but am having trouble adding data to the array.
Dim test As New Dictionary(Of Integer, Dictionary(Of String, String))
Dim test2 As New Dictionary(Of String, String)
Any help is greatly appreciated.
-----EDIT:
Here is what I am using, it works as desired. Doe ayone see why this would be a bad way to do it?
Dim test As New Dictionary(Of Integer, Dictionary(Of String, String))
Dim SomeNum As Integer = 0
Dim someStr As String = "This is a string: "
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
While SomeNum < 100
Dim someNum2 As Integer = 0
Dim test2 As New Dictionary(Of String, String)
While someNum2 < 100
test2.Add(CType(someNum2, String), someStr & CType(someNum2, String))
someNum2 += 1
End While
test.Add(SomeNum, test2)
SomeNum += 1
End While
For Each kvp As KeyValuePair(Of Integer, Dictionary(Of String, String)) In test
Dim ccc As String = ""
Dim ddd As String = ""
Dim v1 As String = CType(kvp.Key, String)
Dim v2 As Dictionary(Of String, String) = kvp.Value
lblOne.Items.Add("Key: " & v1)
For Each kvp2 As KeyValuePair(Of String, String) In v2
Dim v3 As String = kvp2.Key
Dim v4 As String = kvp2.Value
lblTwo.Items.Add("SubKey: " & v3 & " Value: " & v4)
lblOne.Items.Add("")
Next
lblOne.Items.Add(v1 & " End--------------")
lblTwo.Items.Add(v1 & " End--------------")
Next
End Sub
Create a class with properties "Date", "HourlySales", "Category".
Public Class Sales
Public Property SalesDate() As Date
Public Property HourlySales() As Decimal
Public Property Category() As String
Public Sub New()
End Sub
Public Sub New(vSalesDate As Date, vHourlySales As Decimal, vCategory As String)
SalesDate = vSalesDate
HourlySales = vHourlySales
Category = vCategory
End Sub
End Class
Create a list of objects of type Sales
Shared Function GetSales() As List(Of Sales)
Dim SalesList As New List(Of Sales)
Using connection As New SqlConnection(YourConnectionString)
Dim cmd As SqlCommand = New SqlCommand("SelectSalesList", connection)
cmd.CommandType = CommandType.StoredProcedure
cmd.Connection.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
While reader.Read
SalesList.Add(New Sales(reader("SalesDate"), reader("HourlySales"), reader("Category")))
End While
End Using
Return SalesList
End Function
You can call the GetSales() function to return a list of Sales.
Look into Entity Framework it makes objects out of your database.
MSDN EF
For a custom array, you may find a Tuple useful.
A dictionary of dictionary of dictionaries is a maintenance nightmare, to query and debug.
I suggest to have a composite key instead, so instead of doing 3 lookups you would just do one for a string of date+hour+category. For example, date=Monday, hour=9PM, category=Apples, your key is Monday:9PM:Apples (I picked colon as parts separator, but you can choose a different character).

Array to combo box?

I have this structure, with an array of the structure type.
Structure CustomerAccountsRec
Dim strFirstName As String
Dim strLastName As String
Dim intAge As Integer
Dim strAddress As String
Dim strTown As String
Dim strPostcode As String
Dim strCusNum As String
End Structure
Public strCusArray() As CustomerAccountsRec
I want to be able to take the strCusNum of the array and populate a combobox with it but can't figure out how. Any help?
You can also override the ToString Method in your Structure as mentioned. I also created a List(Of CustomerAccountsRec) that makes it a bit easier to add values and then I bound the list to the ComboBox's DataSource
Public Class Form1
Structure CustomerAccountsRec
Dim strFirstName As String
Dim strLastName As String
Dim intAge As Integer
Dim strAddress As String
Dim strTown As String
Dim strPostcode As String
Dim strCusNum As String
Public Overrides Function ToString() As String
Return strCusNum
End Function
End Structure
Public strCusArray As List(Of CustomerAccountsRec) = New List(Of CustomerAccountsRec)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim carec As CustomerAccountsRec = New CustomerAccountsRec
carec.strFirstName = "Hello"
carec.strLastName = "World"
carec.strCusNum = "Hello World"
carec.strTown = "AnyTown"
carec.strAddress = "AnyStreet"
carec.strCusNum = "12345678"
strCusArray.Add(carec)
ComboBox1.DataSource = strCusArray
End Sub
End Class
You can add the items using the ComboBox.Items.Add method, and for the structure to be properly displayed, you have to override it's ToString method.
See:
ComboBox: Adding Text and Value to an Item (no Binding Source)
how to add value to combobox item
You can use LINQ to get an array containing the items you want to display, and then bind that array to the ComboBox.
Dim combo as New ComboBox
combo.DataSource = strCusArray.Select(Function(f) f.strCusNum).ToArray()
If I understand your question.
The actual result of Public strCusArray() As CustomerAccountsRec is null, so we can't use this to add the all items from CustomerAccountsRec to ComboBox
To List all the item from your Structure we need to use the System.Reflection Namespace
Structure CustomerAccountsRec
Dim strFirstName As String
Dim strLastName As String
Dim intAge As Integer
Dim strAddress As String
Dim strTown As String
Dim strPostcode As String
Dim strCusNum As String
End Structure
Dim fi As FieldInfo() = GetType(CustomerAccountsRec).GetFields(BindingFlags.[Public] Or BindingFlags.Instance)
For Each info As FieldInfo In fi
ComboBox2.Items.Add(info.Name)
Next
Source: C# version

vb.net - add object to arraylist

I'm having some trouble adding an object to an arraylist.
Basically the object has two properties (file id/name), but I can't figure out how to assign those properties. During runtime it errors out with public member on the object not found.
Private QueueList As New ArrayList
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
Dim QueueObj As New Object
QueueObj.FileID = "Test 1"
QueueObj.FileName = "Test 2"
QueueList.Add(QueueObj)
End Sub
I'd also like to know how I can do a loop on the arraylist and access the two properites on each record.
Thanks!
You can't just use "Object" for this. You need to build your own class:
Public Class File
Public Property FileID As Integer
Public Property FileName As String
Public Sub New ()
End Sub
Public Sub New(ByVal FileName As String, ByVal FileID As Integer)
Me.FileID = FileID
Me.FileName = FileName
End Sub
End Class
And then build your Queue like this:
Private QueueList As New ArrayList()
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
QueueList.Add(New File(FileName, FileID))
End Sub
Public Sub Queue(ByVal FileObj As File)
QueueList.Add(FileObj)
End Sub
Or, even better, use generics:
Public QueueList As New List(Of File)()
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
QueueList.Add(New File(FileName, FileID))
End Sub
Public Sub Queue(ByVal FileObj As File)
QueueList.Add(FileObj)
End Sub
Then, to loop over list:
For Each item As File In QueueList
'Console.WriteLine(item.FileID & vbTab & item.FileName)
Next item
You need to switch to an object to hold your file information, and drop ArrayList for a strongly typed collection.
public class QueueFile
public Property FileID as integer
public property FileName as string
end class
...
Private QueueList As New List(Of QueueFile)
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
Dim QueueObj As New QueueFile
QueueObj.FileID = "Test 1"
QueueObj.FileName = "Test 2"
QueueList.Add(QueueObj)
End Sub
If you only have two values, you may find using a generic Dictionary even better than an ArrayList (requiring boxing and unboxing the types) or List(Of T) which retains type safety.
Private QueueList As New Dictionary(of Integer, String)
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
QueueList.Add(FileID, FileName)
End Sub
If you really want a Queue as your method name indicates, consider using the generic Queue. Also, if you only need a key/value pair, you don't need to create your own class. You can use the generic KeyValuePair(Of T, S):
Private QueueItems As New Queue(Of KeyValuePair(Of Integer, String))
Public Sub Queue(ByVal FileName As String, ByVal FileID As Integer)
QueueItems.Enqueue(New KeyValuePair(Of Integer, String)(FileID, FileName))
End Sub
To get items out, use the QueueItems.Dequeue.