VB.Net Order a Dictionary(Of String, Integer) by value of the Integer - vb.net

I have a dictionary of String, Integer so the key is the string and the value is the integer and i want to order the keys ascending by the value of the integer. How could I achieve this?

You could use LINQ to sort the Dictionary by value:
Dim dictionary = New Dictionary(Of String, Integer)()
dictionary.Add("A", 2)
dictionary.Add("B", 4)
dictionary.Add("C", 5)
dictionary.Add("D", 3)
dictionary.Add("E", 1)
Dim sorted = From pair In dictionary
Order By pair.Value
Dim sortedDictionary = sorted.ToDictionary(Function(p) p.Key, Function(p) p.Value)
Actually it does not modify the original Dictionary but creates a new Dictionary with the new order.
But: Apart from the feasability, a Dictionary is not an IList (as an Array or List<T>). It's purpose is to lookup a key very efficiently but not to loop all entries.
They are unordered, meaning that although you can retrieve the elements in some order with a foreach loop, that order has no special meaning, and it might change for no apparent reason.

First of all, a dictionary does not have an intrinsic order. It is for look-ups. However, you can turn the keys into their own ordered list.
Dim keyList as List(Of String) = (From tPair As KeyValuePair(Of String, Integer) _
In myDictionary Order By tPair.Value Ascending _
Select tPair.Key).ToList

I had to do something similar to this with custom objects. I think this should be close (but may not be exactly) what you're looking for:
Dim sortedL As List(Of KeyValuePair(Of String, Integer)) = yourDictionary.ToList
sortedL.Sort(Function(firstPair As KeyValuePair(Of String, Integer), nextPair As KeyValuePair(Of String, Integer)) CInt(firstPair.Value).CompareTo(CInt(nextPair.Value)))

Related

How can i change the values in a dictionary that are inside another dictionary?

I've come here after hours of looking on the internet. Nothing comes close to what I am trying to achieve.
I have this:-
Private Portfolio_Client_List As New Dictionary(Of String, Dictionary(Of String, Double))
which I then add keys to like the following:-
Portfolio_Client_List.Add(str.Substring(6, 5).Trim, New Dictionary(Of String, Double))
then I add keys to the other dictionary like the below:-
For Each pair As KeyValuePair(Of String, Dictionary(Of String, Double)) In Portfolio_Client_List
pair.Value.Add("Office Collections", 0.00)
pair.Value.Add("Home Collections", 0.00)
Next
Now I want to update the values in the Office Collections and Home Collections keys values.
How can I do so? I thought it would be as simple as:-
For Each pair As KeyValuePair(Of String, Double) In Portfolio_Client_List("key")
pair.Value += Head_Office_Payments
Next
However it just gives me the readonly error. Is there any way to do this, or am I wasting my time?
No, KeyValuePairs are immutable structs, so you can't modify them, Value is readonly.
But this works (you want to add Head_Office_Payments to the old value):
Dim dict As Dictionary(Of String, Double) = Portfolio_Client_List("key")
For Each key As String In dict.Keys.ToList()
dict(key) += Head_Office_Payments
Next
Note that you need the dict.Keys.ToList(creates a new list) because you can't modify the collection while enumerating, setting the Value of a dictionary increases it's version number which invalidates the iterator. That's why i prefer this one-liner LINQ solution:
dict = dict.ToDictionary(Function(kv) kv.Key, Function(kv) kv.Value + Head_Office_Payments)

how to convert value in a keypairs into list?

Dim dict As New Dictionary(Of String, KeyValuePair(Of String, String))
dict.Add("key", New KeyValuePair(Of String, String)("value1","value2"))
I want to get value2 as a list.
Using LINQ Extensions:
' use KeyValuePair of dictionary, that gives 2 levels of KeyValuePairs
Dim list = dict.Select(function(kvp) kvp.Value.Value).ToList()
' Or use the Values part of Dictionary
Dim list = dict.Values.Select(function(v) v.Value).ToList()
' Adding some conditional and getting value1 instead:
Dim list = dict.Values.Where(function(k) k.Value = "value2").Select(function(v) v.Key).ToList()
For KeyValuePair x.Key is "value1" and x.Value is "value2"
Please note that this will get quite slow on big Dictionarys and there is probably better ways to handle this, but that requires more information about The actual use-case.

How to sort the keys of a dictionary in reverse order using VB.NET?

I have a dictionary:
Dim dicItems As Dictionary(of Integer, String)
The items in the dictionary are:
1,cat
2,dog
3,bird
I would like the order to be:
3,bird
2,dog
1,cat
You can't sort a dictionary, what you need is a sorted list instead.
Dim dicItems As New SortedList(Of Integer, String)
This will sort the items by the key value. If you want to get the items out in descending order like your example you could always do a loop starting from the end of the list, and moving to the beginning.
The below link has more information on SortedList's.
http://msdn.microsoft.com/en-us/library/ms132319%28v=vs.110%29.aspx
You can use LINQ to solve this easily:
Dim dicItems As New Dictionary(Of Integer, String)
With dicItems
.Add(1, "cat")
.Add(2, "dog")
.Add(3, "bird")
End With
dim query = from item in dicItems
order by item.Key descending
select item
If you want, you can also use the Lambda syntax:
Dim query = dicItems.OrderByDescending(Function(item) item.Key)
A dictionary has no implicit order that you can rely on ("The order in which the items are returned is undefined").
As add-on for Shadows answer who suggest to use a SortedList you can get descending order by using the constructor that takes an IComparer(Of Int32):
Dim list = New SortedList(Of Integer, String)(New DescendingComparer())
list.Add(3, "bird")
list.Add(1, "cat")
list.Add(2, "dog")
Public Class DescendingComparer
Implements IComparer(Of Int32)
Public Function Compare(x As Integer, y As Integer) As Integer Implements System.Collections.Generic.IComparer(Of Integer).Compare
Return y.CompareTo(x)
End Function
End Class
Not sure why you would want to, since an order of items in the dictionary usually does not matter, but you can do it like this:
Dim dicItems As New Dictionary(Of Integer, String)
With dicItems
.Add("1", "cat")
.Add("2", "dog")
.Add("3", "bird")
End With
Dim dicItemsReversed As New List(Of KeyValuePair(Of Integer, String))
dicItemsReversed.AddRange(dicItems.Reverse())
Notice that I output to a different collection, i.e. Generic.List in this case. If you want to replace your original contents, you can then do this:
dicItems.Clear()
For Each kv In dicItemsReversed
dicItems.Add(kv.Key, kv.Value)
Next
As a variation on the topic, you can replace dicItems.Reverse() with other LINQ alternatives, such as OrderBy, so you can, for example, sort by Key, Value or a combination thereof. For example this dicItems.OrderBy(Function(x) x.Value) gives the output of:
3,bird
1,cat
2,dog
(sorted alphabetically by value, ascending order)

List of Dictionary Arrays

Hit a wall, and can't find much in docs.
I have two dictionaries, and I'd like to put them in a list.
Dim listOfDictionaries As List(Of Dictionary(Of String, String))
is not working.
Am I correct in assuming that once I get this dimmed, I can .add the conventional way?
Details (EDIT)
When trying to listOfDictionaries.Add(dictionaryIWantToAdd), I get "value of type '1-dimensional array system.collection.generic.dictionary(of string, string)' cannot be converted to 'system.collection.generic.dictionary(of string, string)'
Solution
Helps to put the () on the end an array. :P
The conventional way is:
Dim both = New List(Of Dictionary(Of String, String))()
both.Add(Dictionary1)
both.Add(Dictionary2)
The error says it all. You are trying to add an array of dictionaries to the list, but the add method only takes a single dictionary, not an array of them. Either fix it so you are only passing in a single dictionary:
Dim myDictionary As Dictionary(Of String, String)
' ...
listOfDictionaries.Add(myDictionary)
Or use the AddRange method to add all the dictionaries in the array at once:
Dim myArrayOfDictionaries() As Dictionary(Of String, String)
' ...
listOfDictionaries.AddRange(myArrayOfDictionaries)
I tend to favour single-line solutions when it's something straightforward like this, making use of the From keyword.
Dim listOfDictionaries = New List(Of Dictionary(Of String, String)) From { dictionary1, dictionary2 }

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.