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.
Related
How can this code be accomplished using ToDictionary() instead of a For Each
Dim stringDict As Dictionary(Of String, String) = new Dictionary(Of Integer, String)
Dim stringList As List(Of String) = {"alpha", "beta", "gamma", "delta"}
For Each stringItem As String In stringList
stringDict.Add($"Entry{stringDict.Count+1}",stringItem)
Next
This is what I am trying to do:
Dim stringDict As Dictionary(Of String, String) = stringList.ToDictionary(Function(a) $"Entry{?}", Function(b) b)
I was hoping there might be a variable with the current row or index, or an incrementor
You can use the overload of Select that gives you the index:
Dim stringDict As Dictionary(Of String, String) = stringList.
Select(Function(s, index) (Key:=$"Entry{index + 1}", Value:=s)).
ToDictionary(Function(kv) kv.Key, Function(kv) kv.Value)
However, i find your loop more readable. You should also set Option Strict to On, following should give you a compiler error:
Dim stringDict As Dictionary(Of String, String) = new Dictionary(Of Integer, String)
You can use Enumerable.Range (documentation) to create a range of numbers from 1 to the Count of your List and then Enumerable.ToDictionary (documentation) to convert that range to a Dictionary.
Example:
Dim stringList As New List(Of String) From {"alpha", "beta", "gamma", "delta"}
Dim stringDict = Enumerable.Range(1, stringList.Count).ToDictionary(Function(i) $"Entry{i}", Function(i) stringList.Item(i - 1))
Fiddle: https://dotnetfiddle.net/4xHp9g
I have a SortedDictionary:
Dim myFilterItems As SortedDictionary(Of String, FilterItem)
myFilterItems = New SortedDictionary(Of String, FilterItem)(StringComparer.CurrentCultureIgnoreCase)
The FilterItem class is defined like this:
Private Class FilterItem
Public ValueToSort As Object
Public IsChecked As Boolean
Public IsAbsent As Boolean = False
End Class
I need to enumerate my SortedDictionary sorted by the FilterItem.ValueToSort property. With LINQ, it's easy to do - we get the corresponding IEnumerable and then use For Each:
Dim mySortedValueList As IEnumerable(Of KeyValuePair(Of String, FilterItem))
mySortedValueList = From entry In myFilterItems Order By entry.Value.ValueToSort Ascending
For Each entry As KeyValuePair(Of String, FilterItem) In mySortedValueList
' ...
Next
How to do that effectively in .NET 2.0?
I rewrote my code using Lists as Jon Skeet suggested. As I can see from my tests, I have the same performance like with the LINQ query - or even 5-10% gain. The new version of code looks like this:
Dim mySortedValueList As New List(Of KeyValuePair(Of String, FilterItem))(myFilterItems)
If iArr <> iGAFMValueType.Text Then
mySortedValueList.Sort(AddressOf CompareFilterItemsByValuesToSort)
End If
Private Shared Function CompareFilterItemsByValuesToSort(ByVal itemX As KeyValuePair(Of String, FilterItem), ByVal itemY As KeyValuePair(Of String, FilterItem)) As Integer
Dim myValueX As IComparable = TryCast(itemX.Value.ValueToSort, IComparable)
Dim myValueY As IComparable = TryCast(itemY.Value.ValueToSort, IComparable)
If (myValueX Is Nothing) Then
If (myValueY Is Nothing) Then
Return 0
End If
Return -1
End If
If (myValueY Is Nothing) Then
Return 1
End If
Dim myTypeX As Type = myValueX.GetType()
Dim myTypeY As Type = myValueY.GetType()
If (myTypeX <> myTypeY) Then
Return String.CompareOrdinal(myTypeX.Name, myTypeY.Name)
End If
Return myValueX.CompareTo(myValueY)
End Function
However, while working on this comparison algorithm, I found that I can't compare values of some numeric types - though they can be compared (for instance, SByte and Double values). BTW, the original LINQ query also fails in this case. But this is another story that continues in this question:
How to compare two numeric values (SByte, Double) stored as Objects in .NET 2.0?
I have a problem...
I am trying to put into a list of String dictionary keys values if condition of containsvalue is true:
But, this is not correct :(
here is a code:
Private listID As New List(Of String) ' declaration of list
Private dictionaryID As New Dictionary(Of String, Integer) ' declaration of dictionary
'put a keys and values to dictionary
dictionaryID.Add("first", 1)
dictionaryID.Add("second", 2)
dictionaryID.Add("first1", 1)
If dictionaryID.ContainsValue(1) Then ' if value of dictinary is 1
Dim pair As KeyValuePair(Of String, Integer)
listID.Clear()
For Each pair In dictionaryID
listID.Add(pair.Key)
Next
End If
And now, list must have two elements... -> "first" and "first1"
Can you help me?
Thank you very much!
You are looping through the whole dictionary and add all the elements to the list. You should put an if statement in the For Each or use a LINQ query like this:
If listID IsNot Nothing Then
listID.Clear()
End If
listID = (From kp As KeyValuePair(Of String, Integer) In dictionaryID
Where kp.Value = 1
Select kp.Key).ToList()
Using an if statement:
listID.Clear()
For Each pair As KeyValuePair(Of String, Integer) In dictionaryID
If pair.Value = 1 Then
listID.Add(pair.Key)
End If
Next
My VB.Net is a little rusty, but it looks like you were adding all of them, no matter if their value was 1 or not.
Private listID As New List(Of String) ' declaration of list
Private dictionaryID As New Dictionary(Of String, Integer) ' declaration of dictionary
'put a keys and values to dictionary
dictionaryID.Add("first", 1)
dictionaryID.Add("second", 2)
dictionaryID.Add("first1", 1)
If dictionaryID.ContainsValue(1) Then ' if value of dictinary is 1
Dim pair As KeyValuePair(Of String, Integer)
listID.Clear()
For Each pair In dictionaryID
If pair.Value = 1 Then
listID.Add(pair.Key)
End If
Next
End If
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)))
I know I can do this with a for loop cause that's how i'm doing it now. I was hoping for a more efficient way to accomplish the task.
I have a dictionary(Of Integer, Boolean)
or Of String, Boolean.
i want to get a list(of integer) or Of String from the dictionary where all the values are true(or false depending on what i need at the time)
and to generalize it or "black box" it, it could be any dictionary(of whatever, whatever)
and return a list(of whatever) where the value = whatever i'm looking for at the time.
string, string where value = "Closed"
in short: i want all list of all the keys who's value = some criteria
my current code:
Public Function FindInDict(Of tx, ty)(thedict As Dictionary(Of tx, ty), criteria As ty) As List(Of tx)
Dim tmpList As New List(Of tx)
For xloop As Integer = 0 To thedict.Count - 1
If CType(thedict(thedict.Keys(xloop)), ty).Equals(criteria) Then
tmpList.Add(thedict.Keys(xloop))
End If
Next
Return tmpList
End Function
You can do this easily with Linq:
Public Function FindInDict(Of tx, ty)(thedict As Dictionary(Of tx, ty), criteria As ty) As List(Of tx)
Return (From kvp In thedict
Where kvp.Value.Equals(criteria)
Select kvp.key).ToList()
End Function
Use LINQ, like so:
Dim tStorage As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim tKeys As List(Of String) = New List(Of String)
Dim tCriteria As List(Of String) = New List(Of String)
tStorage.Add("One", "Uno")
tStorage.Add("Two", "Dos")
tStorage.Add("Three", "Tres")
tStorage.Add("Four", "Quatro")
tCriteria.Add("Dos")
tCriteria.Add("Quatro")
tKeys = (From k In tStorage.Keys Where tCriteria.Contains(tStorage(k)) Select k).ToList
For Each tKey As String In tKeys
Console.WriteLine(tKey)
Next
Console.ReadKey()