Grouping Values in a dictionary - vb.net

I have a dictionary that contains a random amount of variables (differs depending on the document it's scanning) in the following format
,A, 500
,ORD, 50000
,ORD, 200
I need to be able to group together all of the values that belong to the same class (ORD being one and A being another) and then add the integers together so that I can output
A - 500
ORD - 50200
So I think I need to assign different integer variables depending on the amount of classes and then add them together but I really don't know how to go about it
EDIT to insert code:
Dim sharenum As Int32 = 0
Dim tempshold As String = ""
For Each sHolder In shareholders
tempshold = Replace(sHolder.numberShares, ",", "")
sharenum = Convert.ToInt32(tempshold)
dicClassShares.Add("," & Trim(sHolder.shareClass) & ",", sharenum)

Change your dictionary structure to a Dictionary(Of String, List(Of Int32))
Then check for the key and either add or edit the dictionary entry depending on the presence of the key:
Dim dicClassShares As Dictionary(Of String, List(Of Int32)) = New Dictionary(Of String, List(Of Int32))
If Not dicClassShares.ContainsKey Then
dicClassShares.Add("," & Trim(sHolder.shareClass) & ",", New List(Of Int32))
End If
dicClassShares("," & Trim(sHolder.shareClass) & ",").Add(sharenum)
And finally obtain the summed value you want...
Dim summedValues As Dictionary(Of String, Int32) =
dicClassShares.ToDictionary(Of String, Int32)(
Function(k) k.Key,
Function(v) v.Value.Sum
)
That last bit is off the top of my head. Play around with it if you get errors.

Related

I am unsure about how to remove cases from the "select case" statement in visual basic after a certain case has been selected more than 3 times

I've a word guessing game in development. It works so that when the play button is clicked, a random number between 1 and 20 will be generated. This new randomly generated number will then go into the select case statement as shown in the code below:
Dim RND As New Random
Dim rndNumber As String
rndNumber = RND.Next(1, 20)
Dim RndWord as string
Dim RndHint as string
select case(rndnumber)
case 1
RndWord = "hockey"
RndHint = "A ball game played with curved, wooden sticks"
Case 2
RndWord = "dinghy"
RndHint = "This is a small boat usually made out of rubber"
These are just 2 out of 20 similar cases.
The selected case contains a word and a hint that will be displayed upon that case being selected. My problem is, how do I remove a case after it has been selected three times; removing the word and the hint from the program completely so that they won't appear again. I've looked into different types of arrays; however, after 2 hours of research and many attempts at using them they don't seem to fit this purpose.
Firstly remove your case statement, it makes is less manageable. What if tomorrow you decide to add 5 more questions? You will end up changing the code. You can keep all the data externally and have them read into a dictionary object.
Define these at class level:
Dim questions As Dictionary(Of Integer, KeyValuePair(Of String, String)) = New Dictionary(Of Integer, KeyValuePair(Of String, String))()
Dim questionAppearedCount As Dictionary(Of Integer, Integer) = New Dictionary(Of Integer, Integer)()
Create new Sub with this:
Public Sub FillQuestions()
questions.Add(1, New KeyValuePair(Of String, String)("hockey", "A ball game played with curved, wooden sticks"))
questions.Add(2, New KeyValuePair(Of String, String)("dinghy", "This is a small boat usually made out of rubber"))
'Also, you may use File.ReadAllLines() to fill from a file.
End Sub
Finally, BtnGenerate_Click
Dim rnd As Random = New Random()
Dim rndNumber As Integer = rnd.[Next](1, 20)
If questionAppearedCount.ContainsKey(rndNumber) Then
questionAppearedCount(rndNumber) = questionAppearedCount(rndNumber) + 1
Else
questionAppearedCount.Add(rndNumber, 1)
End If
If questionAppearedCount(rndNumber) > 3 Then
'do not show question, instead get Next random
Else
return question(rndNumber)
End If
Here is the code, so I keep questions in dictionary, show them using dictionary.
In the above code, question can be read from external file using File.ReadAllLines()
When question is shown, add to the count and if count is > 3, don't show.
Make an array to count each case.
If the count is 3 you loop.
If less you increase the counter in the array and exit the loop so it ll use a case statement.
Be carreful in this case if all counters reach 3 the program wil loop infinitively.
Else an array for all the cases data.
When 3 is reached the datas are moved at the end of the array and the max index is decreased by one. At beginning its 20... next 19.. etc...
I recommend this code.
My sample also has a list of questions.
A very important differences though:
It does the filtering of the data BEFORE the random call. So, it's only searching for the questions that are valid. In certain cases, repeated random calls very close together might produce the same result, so this verifies you only do it once.
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Linq
Public Module Module1
Dim questionAppearedCount As Dictionary(Of Integer, Integer)
Public Sub Main()
Dim questions As Dictionary(Of Integer, KeyValuePair(Of String, String)) = New Dictionary(Of Integer, KeyValuePair(Of String, String))()
questions.Add(1, New KeyValuePair(Of String, String)("hockey", "A ball game played with curved, wooden sticks"))
questions.Add(2, New KeyValuePair(Of String, String)("dinghy", "This is a small boat usually made out of rubber"))
questionAppearedCount = New Dictionary(Of Integer, Integer)()
SelectQuestion(1)
SelectQuestion(1)
SelectQuestion(1)
' Get the Filtered List
Dim filteredQuestions() as KeyValuePair(Of Integer, KeyValuePair(Of String, String)) = questions.Where(Function(ByVal item as KeyValuePair(Of Integer, KeyValuePair(Of String, String))) Not questionAppearedCount.ContainsKey(item.Key) OrElse questionAppearedCount(item.Key) < 3).ToArray()
Dim rand As New Random
Console.WriteLine(filteredQuestions(rand.Next(0, filteredQuestions.Count - 1)).Value)
End Sub
Public Sub SelectQuestion(ByVal questionNumber as Integer)
' Add Question
If questionAppearedCount.ContainsKey(questionNumber) Then
questionAppearedCount(questionNumber) += 1
Else
questionAppearedCount(questionNumber) = 1
End If
End Sub
End Module
And here's some vb.net helper code that you can use to REFACTOR your case statements automatically. Just paste your code into it and it will return the equivalent code to the console. You'll replace your code with this code.
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Linq
Public Module Module1
Dim questionAppearedCount As Dictionary(Of Integer, Integer)
Public Sub Main()
'Dim questions as New Dictionary(Of Integer, KeyValuePair(Of String, String))
For rndnumber as Integer = 1 to 20
Dim RndWord as String = ""
Dim RndHint as String = ""
select case(rndnumber)
case 1
RndWord = "hockey"
RndHint = "A ball game played with curved, wooden sticks"
Case 2
RndWord = "dinghy"
RndHint = "This is a small boat usually made out of rubber"
end SElect
If Not String.IsNullOrEmpty(rndword) Then
'questions.Add(rndnumber, new KeyValuePair(Of String, String)(rndword, rndhint))
Console.WriteLine("questions.Add(" & rndnumber & ", new KeyValuePair(Of String, String)(""" & rndword.Replace("""", """""") & """,""" & RndHint.Replace("""", """""") & """))")
End If
Next
End Sub
End Module

vb.net - How to concatenate a variable(trimmed) to another variable to complete its variable name

Needed code is something like this:
Dim myArray(0) As String
Dim ay As String = "ay"
myArr & ay(0) = "asd"
I've tried but did not worked
Dim classlist1(0) As String
Dim classlist2(0) As String
Dim classlist3(0) As String
Dim classlist4(0) As String
Dim count As Integer = 0
For _year As Integer = 1 To 4
("classlist" & _year)(count) = "hi"
count += 1
Next
Any time you see something like this:
Dim classlist1(0) As String
Dim classlist2(0) As String
Dim classlist3(0) As String
' etc.
It's an indication that you're using the wrong data structure. Instead of trying to dynamically build variable names (which isn't really possible in a static language, at least not without some really ugly reflection code with a high potential for runtime errors), just use a collection.
For example, if you want a collection of strings:
Dim classList As New List(Of String)()
And if you want a collection of collections of strings:
Dim classLists As New List(Of List(Of String))()
Then you can reference the nested lists within the parent list. So to add your first "year" of classes:
classLists.Add(new List(Of String))
And add a class to that year:
classLists(0).Add("some value")
As you can see, it starts to get a little difficult to keep track of the data structures. This is where creating custom types and structures becomes very useful. For example, rather than representing a "year" as a list of strings, create an actual Year class. That class can internally hold a list of strings, and other logic/data.
Try Dictionary<TKey, TValue> Class From MSDN.
Dim classLists As New Dictionary(Of String, String)()
'Add items with keys
For _year As Integer = 1 To 4
classLists.Add(String.Format("classlist{0}",_year), "hi")
Next
And you can get value by key later
Dim key As String = "classlist2"
Dim value As String = classLists(key)

Vb.Net 2D Dictionary - very slow

I need to create a 2D dictionary/keyvalue pair.
I tried something like this.
Dim TwoDimData As New Dictionary(Of String, Dictionary(Of String, String))
'Create an empty table
For Each aid In AIDList '(contains 15000 elements)
TwoDimData.Add(aid, New Dictionary(Of String, String))
For Each bid In BIDList 'contains 30 elements
TwoDimData.Item(aid).Add(bid, "")
Next
Next
'Later populate values.
[some code here to populate the table]
'Now access the value
'The idea is to access the info as given below (access by row name & col name)
Msgbox TwoDimData.Item("A004").Item("B005") ' should give the value of 2
Msgbox TwoDimData.Item("A008").Item("B002") ' should return empty string. No error
Issue:
The issue is in Creating the empty table. It takes 70 seconds to create the TwoDimData table with empty values. Everything else seems to be fine. Is there any way to improve the performance - may be instead of using Dictionary?
I suggest you try Dictionary(Of Tuple(Of String, String), String) instead. That is, the keys are pairs of strings (Tuple(Of String, String)) and the values are strings. That would appear to correspond nicely to the diagram in your question.
Dim matrix As New Dictionary(Of Tuple(Of String, String), String)
' Add a value to the matrix:
matrix.Add(Tuple.Create("A003", "B004"), "3")
' Retrieve a value from the matrix:
Dim valueAtA003B004 = matrix(Tuple.Create("A003", "B004"))
Of course you can define your own key type (representing a combination of two strings) if Tuple(Of String, String) seems too generic for your taste.
Alternatively, you could also just use (possibly jagged) 2D arrays, but that would potentially waste a lot of space if your data is sparse (i.e. if there are many empty cells in that 2D matrix); and you'd be forced to use numeric indices instead of strings.
P.S.: Actually, consider changing the dictionary value type from String to Integer; your example matrix suggests that it contains only integer numbers, so it might not make sense to store them as strings.
P.P.S.: Do not add values for the "empty" cells to the dictionary. That would be very wasteful. Instead, instead of simply retrieving a value from the dictionary, you check whether the dictionary contains the key:
Dim valueA As String = "" ' the default value
If matrix.TryGetValue(Tuple.Create("A007", "B002"), valueA) Then
' the given key was present, and the associated value has been retrieved
…
End If
I would think that a simple structure would suffice for this?
Public Structure My2DItem
Public Row As Integer
Public Col As Integer
Public Value As String
End Structure
Public My2DArray As Generic.List(Of My2DItem) = Nothing
Public Size As Integer
Public MaxRows As Integer
Public MaxCols As Integer
'
Sub Initialise2DArray()
'
Dim CountX As Integer
Dim CountY As Integer
Dim Item As My2DItem
'
'initialise
MaxRows = 15000
MaxCols = 30
Size = MaxRows * MaxCols
My2DArray = New Generic.List(Of My2DItem)
'
'Create an empty table
For CountY = 1 To 15000
For CountX = 1 To 30
Item = New My2DItem
Item.Row = CountY
Item.Col = CountX
Item.Value = "0"
My2DArray.Add(Item)
Item = Nothing
Next
Next
'
End Sub
And to read the data out of the array,
Function GetValue(Y As Integer, X As Integer) As String
'
Dim counter As Integer
'
GetValue = "Error!"
If My2DArray.Count > 0 Then
For counter = 0 To My2DArray.Count - 1
If My2DArray(counter).Row = Y Then
If My2DArray(counter).Col = X Then
GetValue = My2DArray(counter).Value
Exit Function
End If
End If
Next
End If
'
End Function
And to read your sample cell A004 B005
MyStringValue = GetValue(4,5)
I would suggest creating a class that has properties for the AID and BID and use this as the basis for the values you want to store
Public Class AIdBId
Public Property AId As Integer
Public Property BId As Integer
Public Sub New(aId As Integer, bId As Integer)
Me.AId = aid
Me.BId = bid
End Sub
End Class
Note that I have used integers for everything because it seems that is all you need and it is more efficient that using a string
Then you can add values where they are non-zero:
'define your dictionary
Dim valueMatrix As New Dictionary(Of AIdBId, Integer)
'add your values
valueMatrix.Add(New AIdBId(1, 1), 1)
valueMatrix.Add(New AIdBId(2, 3), 1)
valueMatrix.Add(New AIdBId(4, 3), 3)
valueMatrix.Add(New AIdBId(5, 8), 8)
'check if a value exixts
Dim valueExixsts As Boolean = valueMatrix.ContainsKey(New AIdBId(9, 9))
'get a value
Dim i As Integer = valueMatrix(New AIdBId(4, 3))
So you can now combine these two to return the value if there is one or zero if not:
Private Function GetValue(valuematrix As Dictionary(Of AIdBId, Integer), aId As Integer, bId As Integer) As Integer
Dim xRef As New AIdBId(aId, bId)
If valuematrix.ContainsKey(xRef) Then
Return valuematrix(xRef)
Else
Return 0
End If
End Function

VB.net Dictionary loop

Hey all i am in need of some help looping thru my Dictionary list. I can not seem to find the correct syntax in order to do so.
Here is my code:
Dim all = New Dictionary(Of String, Object)()
Dim info = New Dictionary(Of String, Object)()
Dim theShows As String = String.Empty
info!Logo = channel.SelectSingleNode(".//img").Attributes("src").Value
info!Channel = .SelectSingleNode("channel.//span[#class='channel']").ChildNodes(1).ChildNodes(0).InnerText
info!Station = .SelectSingleNode("channel.//span[#class='channel']").ChildNodes(1).ChildNodes(2).InnerText
info!Shows = From tag In channel.SelectNodes(".//a[#class='thickbox']")
Select New With {channel.Show = tag.Attributes("title").Value, channel.Link = tag.Attributes("href").Value}
all.Add(info!Station, info.Item("Shows"))
theShows = all.Item("Shows") '<--Doesnt work...
I just want to extract whatever is in "Shows" from the all dictionary.
Your code,
all.Add(info!Station, info.Item("Shows"))
theShows = all.Item("Shows")
The value of info!Station is being used as the KEY value in the all dictionary. Then you attempt to access the value using the constant string "Shows". I'm not sure what your intention was but
theShows = all.Item(info!Station)
should return the value of Shows that was stored using the Key info!Station.
If you want the list of shows as a string, you can do this,
Dim Shows as String = ""
For Each item in theShows
Shows &= item.Show & vbNewLine
Next
You can loop like this
For Each pair As KeyValuePair(Of String, String) In dict
MsgBox(pair.Key & " - " & pair.Value)
Next
source : VB.Net Dictionary
Winston

Compare two lists 2D and determine differences VB.NET

I declare my 2D lists:
Dim _invoiceitems As New List(Of List(Of String))
Dim _dbitems As New List(Of List(Of String))
Each List is filled like this:
Example Code To fill:
_invoiceitems.Add(New List(Of String))
_invoiceitems(0).Add("Code #")
_invoiceitems(0).Add("Quantity")
Well, now i need a third list called (_changesitems) Note that this result with the differences:
be the result of subtraction of quantities if this is found (dbitems - invoiceitems).
How i can get this result?
The following code will generate the results you are looking for:
Private Function getChangesItems(ByVal invoiceItems As Dictionary(Of String, Integer), ByVal dbItems As Dictionary(Of String, Integer)) As Dictionary(Of String, Integer)
Dim changesItems As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)()
Dim allCodes As List(Of String) = New List(Of String)()
allCodes.AddRange(invoiceItems.Keys)
allCodes.AddRange(dbItems.Keys)
For Each code As String In allCodes
If Not changesItems.ContainsKey(code) Then
Dim dbQuantity As Integer = 0
Dim invoiceQuantity As Integer = 0
If dbItems.ContainsKey(code) Then
dbQuantity = dbItems(code)
End If
If invoiceItems.ContainsKey(code) Then
invoiceQuantity = invoiceItems(code)
End If
Dim changeQuantity As Integer = dbQuantity - invoiceQuantity
If changeQuantity <> 0 Then
changesItems.Add(code, changeQuantity)
End If
End If
Next
Return changesItems
End Function
I used dictionaries instead of lists as was recommended by others. As long as your data only contains a code and a value, the dictionary is a better fit. If you have more columns, I would suggest creating a class that contains properties for each column and then make a list of that class type, rather than a simple 2D list of strings. Doing so would be more type-safe and easier to read.