Using IEnumerable(Of String) to read from different kinds of data sources - vb.net

I am using the below variable to store a list of user ID strings. I then use the list to search for each user using an LDAP query.
Dim userIds As IEnumerable(Of String) =
{"testid1", "testid2", "testid3", "testid4", "testid5"}
That works, but the ID's are hard-coded. How do I make it read the ID's from a ListBox control instead? Would it be something like:
Dim userIds As IEnumerable(Of String) = ListBox1???
I would like to use the ListBox because I will plan to load the ListBox with a bunch of ID's from a text file.
Better yet, is it possible to use a TextBox? If it was a TextBox, I could just copy and paste the ID's that I need to query.

The contents of a ListBox control can be accessed using the ListBox.Items property. It returns a ListBox.ObjectCollection object, which implements IList, ICollection, and IEnumerable.
This is assuming you've added the contents programmatically, rather than binding to a DataSource. If you bound to a DataSource, as LarsTech suggests, you should use ListBox.DataSource.
If you wanted to use a TextBox control, you'd have to manually delimit each ID somehow. You could do this by putting only one ID per line, and then use the Split method to get each ID:
Dim ids as String() = myTextBox.Text.Split(new String() { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

I was going to write this as a comment, but it got a bit long and started involving code examples so I figured it would be better to post it as an answer, even though there is already an accepted answer which is very good.
Since I'm the one who originally gave you the code that used IEnumerable I feel like I should explain why I used it... IEnumerable is the lowest level interface that is implemented by all lists, collections, dictionaries, arrays, etc. Basically, anything that stores multiple data which can be looped through by a For Each loop, implements the IEnumerable interface. In fact, essentially the only thing that the IEnumerable interface supports is the ability to enumerate through its contents with a For Each loop. IEnumerable is just an interface, it's not a concrete object type. Therefore, when you create an IEnumerable variable, that means that variable can be used to reference (i.e. point to) any object that implements that interface (i.e. any object that can be enumerated with a For Each loop.
Therefore, in the following line, it's not creating an IEnumerable type of object. Or at least not in the concrete type sense. It's creating a specific type of object which happens to implement the IEnumerable interface and then sets the ids variable to point to it.
Dim userIds As IEnumerable(Of String) = {"1", "2", "3"}
The phrase {"1", "2", "3"} is actually a literal expression to represent an array of strings. In other words, that literal expression is the equivalent of doing the following:
Dim stringArray(2) As String
stringArray(0) = "1"
stringArray(1) = "2"
stringArray(2) = "3"
So, since the object containing the list of ID's is actually an array of strings, it could have been done like this:
Dim userIds() As String = {"1", "2", "3"}
However, since I wanted the code to work, regardless of the data source, I used the more general IEnumerable interface. Since the only thing that I actually required of the data was that it could be enumerated with a For Each loop, I didn't want to limit the flexibility of the code by requiring the input list of ID's to be of some higher-level specific object type. I didn't really care that the ID's were specifically an array, or a list, or a dictionary, or a collection. As long as it was something that I could loop through, that's all I cared about. By doing so, that made the code more flexible so that you could set the variable to any enumerable data source, such as the Items property of the ListBox. For instance, all of the following would have worked, without changing the rest of the code:
Dim userIds As IEnumerable(Of String) = {"1", "2", "3"}
Or
Dim userIdsArray() As String = {"1", "2", "3"}
Dim userIds As IEnumerable(Of String) = userIdsArray
Or
Dim userIdsArray(2) As String
userIdsArray(0) = "1"
userIdsArray(1) = "2"
userIdsArray(2) = "3"
Dim userIds As IEnumerable(Of String) = userIdsArray
Or
Dim userIds As IEnumerable(Of String) = ListBox1.Items.OfType(Of String)()
Or
Dim userIds As IEnumerable(Of String) = File.ReadAllLines("IDs.txt")
Or
Dim userIds As IEnumerable(Of String) = TextBox1.Text.Split({Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
Etc.
Since all of those above data sources implement the IEnumerable interface, the same userIds variable can be used to reference all of them.

Related

Vb Net check if arrayList contains a substring

I am using myArrayList.Contains(myString) and myArrayList.IndexOf(myString) to check if arrayList contains provided string and get its index respectively.
But, How could I check if contains a substring?
Dim myArrayList as New ArrayList()
myArrayList.add("sub1;sub2")
myArrayList.add("sub3;sub4")
so, something like, myArrayList.Contains("sub3") should return True
Well you could use the ArrayList to search for substrings with
Dim result = myArrayList.ToArray().Any(Function(x) x.ToString().Contains("sub3"))
Of course the advice to use a strongly typed List(Of String) is absolutely correct.
As far as your question goes, without discussing why do you need ArrayList, because array list is there only for backwards compatibility - to select indexes of items that contain specific string, the best performance you will get here
Dim indexes As New List(Of Integer)(100)
For i As Integer = 0 to myArrayList.Count - 1
If DirectCast(myArrayList(i), String).Contains("sub3") Then
indexes.Add(i)
End If
Next
Again, this is if you need to get your indexes. In your case, ArrayList.Contains - you testing whole object [string in your case]. While you need to get the string and test it's part using String.Contains
If you want to test in non case-sensitive manner, you can use String.IndexOf

Dictionary containing an array of strings as value

Is it possible to have a string array as the value in a dictionary? I need to save the description (Hour2) as the key and as value being able to access both the price (elements_PR(4)) and the time offset (1). Is there a good way to do that?
Dim pr_val(2) As String
Dim PR As New Dictionary(Of String, pr_val)
PR.Add("Hour2", {elements_PR(4), "1"})
There is no reason why you can't do it - you could also use values of type Tuple(Of String, String), if the number of elements is fixed in advance - in this case, 2. It'd be easier to perform comparisons on, and would also be immutable, which is often a good idea.
You sure can. Try something like this:
Dim PR As New Dictionary(Of String, IEnumerable(Of String))
PR.Add("Hour2", {elements_PR(4), "1"})
It seems to me that you could create a class representing the price and the time offset. Therefore, you could do something like PR.Add("Hour2", instanceOfClass).
Depending on the meaning of the description in your situation, you could even include it in your class. It would allow you to use another approach with a List(Of YourClass) containing a list of items with the description, the price and the time offset. To retrieve an item by "key", you could then use some Linq.
Short answer - yes. Assuming the following was declared somewhere:
Dim elements_PR(4) As String : elements_PR(4) = "Hello"
Dim PR As New Dictionary(Of String, String())
You can either do:
PR.Add("Hour2", {elements_PR(4), "1"})
or
Dim pr_val() As String = {elements_PR(4), "1"}
PR.Add("Hour1", pr_val)

List with different object types?

Can I have a List containing one string and two numbers? Or I can only have one type of element?
If that's the kind of functionality you want, then I would look at the non-generic System.Collections.ArrayList class.
Update
For those of you who aren't going to read the huge comment chain...it looks like Adam Robinson is on to something using List<object> over ArrayList. Both will work but on large collections it seems like List<object> is measurably faster than ArrayList.
You can. A list of Objects can do that. But, you lose type safety with that and also design time intelliSense.
What do you want to do? You could also use a class with 3 members.
No, containers like List(Of T) store exactly one type T of elements. You can, though, make this one type consist of one string and two numbers.
Structure Foo
Public Desc As String
Public x As Integer, y As Integer
End Structure
Dim List = New List(Of Foo)
Yes, you can.
dim myVehicles as new list(of object)
dim myCar as new car
dim myBike as new bike
dim mySecondCar as new car
myVehicles.add(myCar)
myVehicles.add(myBike)
myVehicles.add(mySecondCar)

VB.NET - Find a Substring in an ArrayList, StringCollection or List(Of String)

I've got some code that creates a list of AD groups that the user is a member of, with the intention of saying 'if user is a member of GroupX then allow admin access, if not allow basic access'.
I was using a StringCollection to store this list of Groups, and intended to use the Contains method to test for membership of my admin group, but the problem is that this method only compares the full string - but my AD groups values are formatted as cn=GroupX, etc....
I want to be easily able to determine if a particular substring (i.e. 'GroupX') appears in the list of groups. I could always iterate through the groups check each for a substring representing my AD group name, but I'm more interested in finding out if there is a 'better' way.
Clearly there are a number of repositories for the list of Groups, and it appears that Generics (List(Of String)) are more commonly preferred (which I may well implement anyway) but there is no in-built means of checking for a substring using this method either.
Any suggestions? Or should I just iterated through the list of groups?
RESULT:
I've settled on using a List(Of), and I've borrowed from Dan's code to iterate through the list.
I don't think you're going to find a better method than enumerating over the collection*.
That said, here's a good way to do it that will be independent of collection type:
Public Function ContainsSubstring(ByVal objects As IEnumerable, ByVal substring As String) As Boolean
Dim strings = objects.OfType(Of String)()
For Each str As String in strings
If str.Contains(substring) Then Return True
Next
Return False
End Function
This is a good way to address the "which collection to use?" issue since basically all collections, generic or not (ArrayList, List(Of String), etc.), implement IEnumerable.
*Justification for why I believe this forthcoming.
Writing a helper function which will iterate through the items checking for substrings and returning you a Boolean flag seem to be your best bet.
You can use a predicate function for that. It's a boolean function which will help you to filter out some entries.
For example, to get non-hidden files from a list:
Public ReadOnly Property NotHiddenFiles As List(Of FileInfo)
Get
Dim filesDirInfo As New DirectoryInfo(FileStorageDirectory)
Return filesDirInfo.GetFiles.ToList.FindAll(AddressOf NotHiddenPredicate)
End Get
End Property
Private Function NotHiddenPredicate(ByVal f As FileInfo) As Boolean
Return Not ((f.Attributes And FileAttributes.Hidden) = FileAttributes.Hidden)
End Function

Iterate through generic list of unknown type at runtime in VB.Net

Does anyone know how to iterate over a generic list if the type of that list isn't known until runtime?
For example, assume obj1 is passed into a function as an Object:
Dim t As Type = obj1.GetType
If t.IsGenericType Then
Dim typeParameters() As Type = t.GetGenericArguments()
Dim typeParam As Type = typeParameters(0)
End If
If obj is passed as a List(Of String) then using the above I can determine that a generic list (t) was passed and that it's of type String (typeParam). I know I am making a big assumption that there is only one generic parameter, but that's fine for this simple example.
What I'd like to know is, based on the above, how do I do something like this:
For Each item As typeParam In obj1
'do something with it here
Next
Or even something as simple as getting obj1.Count().
The method that iterates over your list can specify a generic type:
Public Sub Foo(Of T)(list As List(Of T))
For Each obj As T In list
..do something with obj..
Next
End Sub
So then you can call:
Dim list As New List(Of String)
Foo(Of String)(list)
This method makes the code look a little hairy, at least in VB.NET.
The same thing can be accomplished if you have the objects that are in the list implement a specific interface. That way you can populate the list with any object type as long as they implement the interface, the iteration method would only work on the common values between the object types.
If you know that obj is a Generic List. Then you're in luck.
Generic List implements IList and IEnumerable (both are non-generic). So you could cast to either of those interfaces and then For Each over them.
IList has a count property.
IList also has a Cast method. If you don't know the type to cast to, use object. This will give you an IEnumerable(Of object) that you can then start using Linq against.