What can I do with the IEnumerable(of T) result except iterate over it? - vb.net

Ok, I'm new to Linq and I'm using VB.NET. Given a list of objects that has 2 properties called AttributeVariable and AttributeValue, I want to select the AttributeValue for the first item in a collection that has a specific AttributeVariable value. This is my start:
Dim query = From c In items
Where c.AttributeVariable = "thename"
Select c.AttributeValue
Cool, it works and I can for each over the query results and write out the result.
Since c.AttributeValue is a String, what is the simplest way to assign the first item in the list (there is only one) to a string variable?

How about FirstOrDefault? (There is also SingleOrDefault and overloads that take default values; consult the documentation.)
Returns the first element of a sequence, or a default value if the sequence contains no elements.
Then:
Dim attribute = (From c In items
Where c.AttributeVariable = "thename"
Select c.AttributeValue).FirstOrDefault()
The key to this is the attribute value (a String) is selected before the FirstOrDefault - thus the resulting type of the expression is a String.
In any case, while I don't believe it is possible to do this with just query syntax, that does not pose an issue because the (query) returns an IEnumerable which can then be used with a "normal" Enumerable extension method as shown.

Related

How to reduce dimension?

Let us consider that I have an IEnumerable(Of IEnumerable(Of Integer)). All the inner IEnumerables contain IDs. I would like to gather all the IDs Distinctly into an IEnumerable(Of Integer). I can do that if I do something like this:
Dim result as New List(Of Integer)
For Each element In IDs
result.AddRange(element)
Next
result = result.Distinct
I have two problems with this approach:
- I have to write the iteration code when there is probably a Function for this purpose that I am unaware of
- The result is a List and I would like to keep the class of the inner IEnumerable
Is there a handy LINQ way to do this?
EDIT:
This question is different from this one, since I am speaking about IEnumerables, not Lists. Yes, I am aware that every List is an IEnumerable, but I am also aware that not every IEnumerable is a List. As a result, this is a more general question than the one which was marked as duplicate, as I have specified in my original question that I would like to maintain the Class of the inner IEnumerable in the result. If it was an array, I prefer to use an array.
You could use SelectMany extension method to flatten the structure, once flatten look for Distinct
result = IDs.SelectMany(Function(x) x).Distinct();
check this Demo

Unable to pull fields from within LINQ query

Quick overview:
Visual Basic 2010 WinForm app pulls data from DB2. The app allows users to filter the data.
The problem:
I'm doing something wrong with my LINQ query (or object definition), as I'm not able to access fields within the dataset. Pulling data from DB2 is fine, I get that data and store it as an IEnumerable.
I plan to run this app as disconnected since its readonly for 95% of the users and it accesses 100,000+ records. Because of this, I have two datasets: 1) 'data' which is the full dataset pulled from DB2 (I don't plan on doing any modifications to it), (2) 'filteredData' which is a subset of data based on user entered filters.
Dim data As IEnumerable
Dim dataFiltered = From record in data
Select record
'Filter data based on version
Select case uxCodeVersion.Text
Case "10"
dataFiltered = From rec in dataFiltered
Where rec.
... (other parts of case statement removed)
End Select
and this is my problem. I'm expecting to see the list of fields within 'rec.' (such as rec.CodeVersion) ;however, I just receive object methods (Equals, GetHashCode, GetType, ReferenceEquals, ToString).
What simple thing am I missing?
Performance is an issue too, but I figured one problem at a time...
Thank you,
Brian.
Here is the answer as provided below.
When defining data, I need to define it to the generic list DTO. So in my case, that becomes:
Dim data As IEnumerable(Of DataAccessLayer.DiagnosisAndDiagnosisIndustryCombinedDTO)
Then when accessing the code, its the same as before, though I temporarily took out the dataFiltered field and just used data.
dataFiltered = From rec in data
Where rec.CodeVersion = uxCodeVersion.Text
From your code sample, you define data as IEnumerable. From your comment you say that your data layer returns a List(of T),
While the assignement of List(of T) to IEnumerable is valid, the type contained in the IEnumerable is Object. That's why you don't get intellisense.
You should either declare IEnumerable(of T), or do something like:
Dim data = datalayer.GetFoo()
This will cause type inference and you'll get intellisense.
Until you assign a type to IEnumerable, you won't be able to access the fields in this way. For example (c#): IEnumerable<YourType> should work.

Is it possible to get the nth item in a tuple?

I am passing a few optional arguments to a function as a tuple, since all of these have to be passed together or not at all. I would like to be able to iterate over the elements of the tuple numerically, and perform an operation on each item. For example:
Public Function myFunction(Optional t As Tuple(Of Integer, String, SomeType) = Nothing) As Integer
For i = 0 to 2
someCollection(i).someMethod(t(i)) 'Pseudocode for accessing ith item in tuple
Next
End Function
One way to resolve the problem would be to use a list, but then I lose the ability to enforce the number of members (which will always be fixed) and the types of each member. Another way would be to write out the statement three times with t.Item1, t.Item2 etc, but this is ugly.
Is there any way to access the nth item in a tuple?
Note: I would like to accomplish this with a tuple if at all possible, even though I am aware I could create alternate method signatures.
(Sure, I’ll turn this into an answer!)
You can put the items into an array for convenience; maintaining the type isn’t really an issue at that point, since if you’re doing the same thing with all of them they need to have some sort of common base class or interface.
Dim a() As Object = {t.Item1, t.Item2, t.Item3}
Then just iterate over that.

VB.NET: Writing a function that take specific-type, multiple key-value parameters

I'm trying to write a function in a basic MVC (experimental project) that should be called with the following (or as close to):
datastore = myTable.FetchData({{column1, constraint1},
{column2, constraint2}, ...
{colN, conN}})
Function purpose - It queries a table with the constraints passed to it. For example,
FetchData({{Me.Fields("Price"), "<100"}, {Me.Fields("Type"), "=Component"}})
will ultimately produce the query
SELECT * FROM table a WHERE Price < 100 AND Type = "Component"
(the query production is more involved than that, involving relationships defined and so forth, but that's outside the scope of this question)
How should I write the function definition to take these parameters?
`Public Function FetchData( ??? ) As foobar
Essentially, it will be something similar to a Dictionary, since it's a list of pairs of values. However, it needs to be non-unique (it could be called to produce col1 > 1 AND col1 < 5 for eg). A two-dimensional arraylist has been considered also, but each half of the pair needs to be a specific type - the 'key' needs to be of my ColumnDefinition object type or string, the 'value' should always be a string.
What would be the best way to handle this?
Bonus question: merging the operator in with the constraint ("=component") seems ugly. Any idea about how to write the function def with the operator separate? I tried an enum but that just made it too verbose - I want this to be a fairly easy to use library.
If you are on .NET 4.0 or higher, suggest trying out the Tuple class to represent your queries. Your code may look like below.
Public Function FetchData(ByVal params As List(Of Tuple(Of ColumnDefinition, Char, String))) As foobar
Tuples are only recommended for API's under your control, where the context is obvious. If this is a public or shared API, suggest creating a named class with appropriate properties (as in Nico Schertler's comment). Then, the code may look like.
Public Function FetchData(ByVal params As List(Of MyContainerClass)) As foobar
Hope this helps.
Or according to the shape of the function call you are describing, if you are definitely using strings, and it is always {"Col", "Constraint"} then you could do this
Public Function FetchData(ByVal MultiDimensionArray(,) As String)
'this is then how you could pull the pairs of cols and constraints back out
'where n is the col constraint pair count up to (n) number
For n As Integer = 0 To MultiDimensionArray.Length - 1
Dim Col As String = MultiDimensionArray(0, n)
Dim Constraint As String = = MultiDimensionArray(1, n)
Next
End Function

Is there any Count method for IEnumerable in VB.NET?

In C# if I have the following object:
IEnumerable<Product> products;
and if I want to get how many elements it contains I use:
int productCount = products.Count();
but it looks like there is no such method in VB.NET. Anybody knows how to achieve the same result in VB.NET?
Count is available in VB.NET:
Dim x As New List(Of String)
Dim count As Integer
x.Add("Item 1")
x.Add("Item 2")
count = x.Count
http://msdn.microsoft.com/en-us/library/bb535181.aspx#Y0
In later versions of .net, there is an extension method called Count() associated with IEnumerable<T>, which will use IList<T>.Count() or ICollection.Count() if the underlying enumerator supports either of those, or will iteratively count the items if it does not.
An important caveat not always considered with this: while an IEnumerable<DerivedType> may generally be substituted for an IEnumerable<BaseType>, a type which implements IList<DerivedType> but does not implement ICollection may be efficiently counted when used as an IEnumerable<DerivedType>, but not when cast as IEnumerable<BaseType> (even though the class would support an IList<DerivedType>.Count() method which would return the correct result, the system wouldn't look for that--it would look for IList<BaseType> instead, which would not be implemented.
In general, IEnumerable won't have a Count unless the underlying collection supports (eg List).
Think about what needs to happen for a generic IEnumerable to implement a Count method. Since the IEnumerable only executes when data is requested, in order to perform a Count, it needs to iterate through till the end keeping track of how many elements it has found.
Generally, this iteration will come to an end but you can setup a query that loops forever. Count is either very costly time-wise or dangerous with IEnumerable.