Filter a Dictionary to return a list - vb.net

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()

Related

Convert a List to a Dictionary with a unique numbered key

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

How to sort SortedDictionary by values in .NET 2.0?

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?

VB dictionary contains value return key

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

List(of String) or Array or ArrayList

Hopefully a simple question to most programmers with some experience.
What is the datatype that lets me do this?
Dim lstOfStrings as *IDK*
Dim String0 As String = "some value"
Dim String1 As String = "some value"
Dim String2 As String = "some value"
Dim String3 As String = "some value"
Dim String4 As String = "some value"
Dim String5 As String = "some value"
lstOfStrings.add(String0, String1, String2, String3)
I would access these like this
Dim s1 = lstOfStrings(0)
Dim s2 = lstOfStrings(1)
Dim s3 = lstOfStrings(2)
Dim s4 = lstOfStrings(3)
if I use List(of String)
I am only able to .add one thing to the list (at a time), and in my function I want to be able to store several values(at a time).
Solution:
Private Function Foo() As List(Of String)
Dim temp1 As String
Dim temp2 As String
Dim temp3 As String
Dim temp4 As String
Dim temp5 As String
Dim temp6 As String
Dim inputs() As String = {temp1, temp2, temp3, temp4, temp5, temp6}
Dim lstWriteBits As List(Of String) = New List(Of String)(inputs)
Return lstWriteBits
End Function
List(Of String) will handle that, mostly - though you need to either use AddRange to add a collection of items, or Add to add one at a time:
lstOfString.Add(String1)
lstOfString.Add(String2)
lstOfString.Add(String3)
lstOfString.Add(String4)
If you're adding known values, as you show, a good option is to use something like:
Dim inputs() As String = { "some value", _
"some value2", _
"some value3", _
"some value4" }
Dim lstOfString as List(Of String) = new List(Of String)(inputs)
' ...
Dim s3 = lstOfStrings(3)
This will still allow you to add items later as desired, but also get your initial values in quickly.
Edit:
In your code, you need to fix the declaration. Change:
Dim lstWriteBits() As List(Of String)
To:
Dim lstWriteBits As List(Of String)
Currently, you're declaring an Array of List(Of String) objects.
You can do something like this,
Dim lstOfStrings As New List(Of String) From {"Value1", "Value2", "Value3"}
Collection Initializers
Neither collection will let you add items that way.
You can make an extension to make for examle List(Of String) have an Add method that can do that:
Imports System.Runtime.CompilerServices
Module StringExtensions
<Extension()>
Public Sub Add(ByVal list As List(Of String), ParamArray values As String())
For Each s As String In values
list.Add(s)
Next
End Sub
End Module
Now you can add multiple value in one call:
Dim lstOfStrings as New List(Of String)
lstOfStrings.Add(String1, String2, String3, String4)
look to the List AddRange method here
Sometimes I don't want to add items to a list when I instantiate it.
Instantiate a blank list
Dim blankList As List(Of String) = New List(Of String)
Add to the list
blankList.Add("Dis be part of me list") 'blankList is no longer blank, but you get the drift
Loop through the list
For Each item in blankList
' write code here, for example:
Console.WriteLine(item)
Next
You can use IList(Of String) in the function :
Private Function getWriteBits() As IList(Of String)
Dim temp1 As String
Dim temp2 As Boolean
Dim temp3 As Boolean
'Pallet Destination Unique
Dim temp4 As Boolean
Dim temp5 As Boolean
Dim temp6 As Boolean
Dim lstWriteBits As Ilist = {temp1, temp2, temp3, temp4, temp5, temp6}
Return lstWriteBits
End Function
use
list1.AddRange(list2) to add lists
Hope it helps.
For those who are stuck maintaining old .net, here is one that works in .net framework 2.x:
Dim lstOfStrings As New List(of String)( new String(){"v1","v2","v3"} )

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.