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
Related
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.
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.
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.
I would like to sort a List(Of RectangleF) somehow.
It is going to be huge, so I would rather not have to create a separate class that implements Comparable -
Is it possible to somehow override the RectangleF itself, to add a Comparable, and ovverride a CompareTo to compare by X ?
I am using VB.NET, but I would appreciate C# advice as well.
You can use LINQ's OrderBy methods to sort however you choose.
For example, if you want to order the list by X, you could do:
Dim orderedByX = theList.OrderBy(Function(rect) rect.X)
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.