Single LINQ query over 3 separate collections
I have an object that has 3 separate dictionaries. The value parameter for each dictionary implements the same interface. What is the best way to combine 3 dictionaries into one and execute one query so that the query results are a single IEnumerable?
Here's a rough idea of what I'm trying to accomplish. My object DataSet
contains 3 dictionaries, each of which should be very small (in theory, some could contain up to 100 elements, but except in extreme cases they will always be less than 20 and usually 6 or less).
The purpose of the method GetAllId()
is to get the Id for multiple private fields in each element of each dictionary and return it as a single IEnumerable. Dictionary value objects all implement IIdQueryable, which defines a single method that will retrieve everything required Id
in the object.
I have two different ideas on how to accomplish what I want, but I'm not sure if there is a better way to do this?
public class DataSet
{
Dictionary<Int32, Foo> dict1;
Dictionary<CustomKey, Bar> dict2;
Dictionary<Int32, Boo> dict3;
public IEnumerable<Int32> GetAllId
{
// need to retrieve Id from dict1, dict2, and dict3.
// implementation ideas below
}
}
Option 1
public IEnumerable<Int32> GetAllId
{
var q1 = dict.Values.SelectMany(g => g.GetId());
var q2 = dict.Values.SelectMany(g => g.GetId());
var q3 = dict.Values.SelectMany(g => g.GetId());
return q1.Concat(q2).Concat(q3);
}
Option 2
public IEnumerable<Int32> GetAllId
{
var c1 = dict1.Values.Cast<IIdQueryable>();
var c2 = dict2.Values.Cast<IIdQueryable>();
var c3 = dict2.Values.Cast<IIdQueryable>();
var collection = c1.Concat(c2).Concat(c3);
return collection.SelectMany(g => g.GetId());
}
Method # 3
Since every object implements the same interface, is it possible to execute one LINQ query on all three objects without casting?
I personally like Method # 1 better as it does not include casting, but I think Method # 2 looks more readable.
If needed, here is a rough idea of how the interface is implemented
public interface IIdQueryable
{
IEnumerable<Int32> GetId();
}
public class Foo : IIdQueryable
{
public IEnumerable<Int32> GetId()
{
//returns Id of all elements in this object
}
}
public class Bar : IGuidQueryable
{
public IEnumerable<Int32> GetId()
{
//returns Id of all elements in this object
}
}
public class Boo : IGuidQueryable
{
public IEnumerable<Int32> GetId()
{
//returns Id of all elements in this object
}
}
EDIT:
The question title is the source of what I was hoping to do (i.e. do all 3 searches in one query without casting). I have explained this above.
source to share
You only need one call SelectMany
in the first approach:
public IEnumerable<Int32> GetAllId()
{
return dict1.Values
.Select(x => x.GetId())
.Concat(dict2.Values.Select( x=> x.GetId()))
.Concat(dict3.Values.Select(x => x.GetId()))
.SelectMany(x => x);
}
Personally, I wouldn't glue this together, although the performance impact had no effect on keeping separate queries and just returning the concatenation like you did in the first example - this is more readable to me:
public IEnumerable<Int32> GetAllId()
{
var q1 = dict1.Values.Select(g => g.GetId());
var q2 = dict2.Values.Select(g => g.GetId());
var q3 = dict3.Values.Select(g => g.GetId());
return q1.Concat(q2)
.Concat(q3)
.SelectMany(x => x);
}
Now it looks pretty close to the second set already, but no throw is required.
source to share