Single LINQ Query on 3 separate collections - c#

I have an object that has 3 separate Dictionaries. The value parameter for each dictionary implements the same interface. What is the best method to combine the 3 dictionaries into one and perform a single query so the results from the query will be a single IEnumerable?
Here's a rough idea of I am trying to accomplish. My DataSet object contains 3 dictionaries, each of which should be very small (theoretically some could contain up to 100 elements, but except in the most extreme cases they will be always less than 20 and usually 6 or less).
The purpose of the GetAllId() method will be to retrieve the Id for several private fields in each element of each dictionary and return it as a single IEnumerable. The dictionary value objects all implement IIdQueryable, which defines a single method that will extract all of the required Id's in the object.
I have 2 different ideas on how to accomplish what I want, but I am not sure if there is a better way to accomplish 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 each object implements the same interface, is it possible to perform a single LINQ query on all 3 objects without casting?
I personally like Method #1 better as it doesn't involve casting anything, but I think Method #2 seems to be more readable.
If it is needed, here's 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 could be done (that is do all 3 lookups in a single query without casting). I clarified that above.

You just need one SelectMany call 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 duct-tape this together though, there is no performance impact by keeping the queries separate and just returning the concatenation like you did already in the first example - it 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 this looks pretty close to the second approach already - but no cast needed.

Related

Selecting objects from two lists of objects [duplicate]

This question already has answers here:
Linq Except considering only one property
(2 answers)
Closed 6 years ago.
I have a method that takes two lists as arguments and returns a list with the objects shared by the two arguments by comparing their name. It looks like this:
static List<Department> InCommon(List<Department> list_a, List<Department> list_b)
{
List<Department> a_list = new List<Department>();
var names = list_b.Select(item => item.Name);
a_list = list_a.Where(item => names.Contains(item.Name)).ToList();
return a_list;
}
I need a second method that does something similar. It should be able to return the objects in list_a after having removed the objects in list_b from it. list_a minus list_b, so to speak. I suspect
List<T>.Except
might be the solution, but how can I achieve this?
To use Except you would need to implement an IEqualityComparer<Department> that compares two Department instances by their Name properties.
So an easier way may be this:
return list_a.Where(a => list_b.All(b => a.Name != b.Name)).ToList();
This takes all elements from list_a with a Name that does not occure in list_b.
For the Except version you would need an IEqualityComparer<Department> like this:
public class DepartmentByNameComparer : IEqualityComparer<Department>
{
public bool Equals(Department x, Department y)
{
return x.Name == y.Name;
}
public int GetHashCode(Department obj)
{
return obj.Name.GetHashCode();
}
}
Then you could call Except like this:
return list_a.Except(list_b, new DepartmentByNameComparer()).ToList();

Is there a way to simplify initializing Linq new object initialization?

I find that I am repeating a lot of new object initialization code in Linq queries, for example when creating different overloaded methods that use the same query structure.
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(
grouped => new
{
ItemID = grouped.Key,
Average = (double)grouped.Average(g => g.OptionValue),
...etc. lots of properties, similar structure across lots of methods...
...Would really love to be able to write this code once somewhere...
}
);
At first I thought using constructors might be one way of doing it, something along these lines:
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(grouped => new TestClass(grouped) //or anonymous type
);
public class TestClass
{
public int ItemID { get; set; }
public double Average { get; set; }
public TestClass() {}
public TestClass(IGrouping<int, ItemRespons> values)
{
ItemID = values.Key;
Average = values.Average(g => g.OptionValue);
}
}
But I see that Linq (to Entities at least) only allows parameterless constructors and initializers. So this approach doesn't seem to work.
Is there another way I can achieve simplifying this type of repetive code, and only having it in one place?
Use a delegate:
Func<IQueryable<ItemResponse>,IEnumerable<TestClass>> SelectResult = q =>
q.GroupBy(ir => ir.ItemID)
.Select(
grouped => new TestClass
{
ItemID = grouped.Key,
Average = (double)grouped.Average(g => g.OptionValue),
...
});
Then you can use it like this:
var result = SelectResult(ctx.ItemResponse);
It's even better to make it a extension method of course:
public static class Extensions
{
public static IEnumerable<TestClass> SelectResult(this IQueryable<ItemResponse> q)
{
return q.GroupBy(ir => ...)
}
}
And use it like this:
var result = ctx.ItemResponses.SelectResult();
It's not possible for anonymous type projections because there is no way to define a typed result, except some non generic type like dynamic, object or IQueryable, but then you'll have problem consuming it.
However it is possible to reuse projections to a custom types (like your sample TestClass). But instead of constructor, you have to put the code in a expression returning method.
For instance, instead of this
public TestClass(IGrouping<int, ItemResponse> values)
{
ItemID = values.Key;
Average = values.Average(g => g.OptionValue);
// ...etc. lots of properties
}
you could use something like this
static Expression<Func<IGrouping<int, ItemResponse>, TestClass>> ToTestClass()
{
return values => new TestClass
{
ItemID = values.Key,
Average = values.Average(g => g.OptionValue)
// ...etc. lots of properties
};
}
and the sample query would be
var result = ItemResponses
.GroupBy(ir => ir.ItemID)
.Select(ToTestClass());

C# Create a list of objects from an 2 IEnumerable instances containing only those objects with a value found in the second list

I have 2 different classes:
public class ClassOne
{
public string ClassOneID { get; set; }
...
}
public class ClassTwo
{
public string ClassTwoID { get; set; }
...
}
I have IEnumerable instances of each. I want to return a List<ClassOne> that contains only the ClassOne items whose ClassOneID is equal to the ClassTwoID of a ClassTwo object from the second IEnumerable instance (if that makes sense!). I was thinking the following:
var list = new List<ClassOne>();
list.AddRange(classOneEnumerable.Where(o =>
classTwoEnumerable.Select(c => c.ClassTwoID == o.ClassOneID).First()));
This logic is contained within code that is some days off building/testing, so I am not actually able to run it just yet. I am not sure if what I have come up with is actually correct, and was hoping someone could put me right if I am mistaken.
var list = (from classOne in classOneEnumerable
from classTwo in classTwoEnumerable
where classOne.ClassOneID == classTwo.ClassTwoID
select classOne).ToList();
var list2 = (from classOne in classOneEnumerable
join classTwo in classTwoEnumerable
on classOne.ClassOneID equals classTwo.ClassTwoID
select classOne).ToList();
Both queries will yield the same results.
The existing answers are fine if you can handle O(n2). Otherwise I would sort the inner values so that you can get n log(n) performance.
Try this
var list = classOneEnumerable.Where(o => classTwoEnumerable
.Any(c => c.ClassTwoID == o.ClassOneID)))
.ToList();

Casting to a derived type in a LINQ to Entities query with Table Per Hierarchy inheritance

I have a LINQ to entities model with Table Per Hierarchy inheritance. I have a query over the base type, and I want to do specific type-dependent logic. For example:
IQueryable<BaseType> base = ...
// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();
// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();
Is there any way to do something like this without processing IQueryables of each derived type separately:
// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();
Direct casting to an entity type like (DerivedType1)b isn't supported with LINQ-to-Entities but the as operator (b as DerivedType1) is, hence you could try:
var result2 = base
.Select(b => b is DerivedType1
? (b as DerivedType1).DerivedProperty
: null)
.ToList();
OfType<DerivedType1>()
will return an IEnumerable, if possible, try to change to base-type to IEnumerable instead of IQueryable, you might en up in some SQL restrictions when using IQueryable.
That is of course if you are not actually quering a database?
You can use EntityFramework.Extended to improve the performance of the query instead of doing 2 round trips to DB.
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();
In this case only one round trip to bd is executed.
This framework is very useful for many other things int EF
You could have a method on your base type that's overridden in your derived types to provide the relevant property value.
public class MyBaseClass
{
public virtual int GetSomething()
{
throw new NotImplementedException();
}
}
public class MyDerivedClass1 : MyBaseClass
{
public int SomeProperty { get; set; }
public override int GetSomething()
{
return this.SomeProperty;
}
}
public class MyDerivedClass2 : MyBaseClass
{
public int SomeOtherProperty { get; set; }
public override int GetSomething()
{
return this.SomeOtherProperty;
}
}
Then you could:
var result = base.Select(b => b.GetSomething()).ToList();
Try this, I have never done anything with needing to do this kind of this but this should do it. Also if you use base, first of all don't because it is a keyword but if you must, use #base the # in front of the name denotes that it is not used as a keyword.
var resultA = base.Select(aVar =>
(aVar is DerivedType1) ?
(DerivedType)(((DerivedType1)aVar).DerivedProperty)
:
(DerivedType)(default(int?))
).ToList();

LINQ to select items in collections with differing types

How would one implement LINQ to extract the Guid's from one collection of objects of type A such that they can exclude these Guids from another collection of objects of type B. Object A and Object B both have a Guid field called 'ID."
I have the following:
ObservableCollection<Component> component Component has a
field called ID of type Guid
ObservableCollection<ComponentInformation> ComponentInformationCollection ComponentInformation
has a field called ID of type Guid
My implementation:
component =>
{
if (component != null)
{
var cancelledComponents = new List<ComponentInformation>();
foreach (Component comp in component)
{
cancelledComponents.Add(new ComponentInformation() { ID = comp.ID });
}
this.ComponentInformationCollection.Remove(cancelledComponents);
}
});
I believe there is a more elegant solution which I've been working at to solve but the issue I keep running into is creating a 'new ComponentInformation' such that the types do not give me an error.
====== FINAL SOLUTION =======
var cancelledComponentIDs = new HashSet<Guid>(component.Select(x => x.ID));
this.ComponentInformationCollection.Remove(
this.ComponentInformationCollection.Where(x => cancelledComponentIDs.Contains(x.ID)).ToList());
Thank you to:
Jason - I used this as a template for my final solution (listed below).
Servy - While I could have used a comparer, I think for this particular scenario a comparer was not neccessary because of its one-time-use type of situation.
ComponentInformationCollection is a Silverlight DependencyProperty that will trigger a INotifyChangedEvent (MVVM pattern) when altered, so the solution above worked best for my situation.
I would do this:
var ids = new HashSet<Guid>(
component.Select(x => x.ID)
);
var keepers = ComponentInformationCollection.Where(x => !ids.Contains(x.ID));
If Component doesn't already define an Equals and GetHashCode that uses the ID to do the compare you can define a comparer such as this:
class ComponentComparer : IEqualityComparer<Component>
{
public int Compare(Component a, Component b)
{
return a.ID.CompareTo(b.ID);
}
public int GetHashCode(Component a)
{
return a.ID.GetHashCode();
}
}
Then you can just use:
var result = componentCollectionA.Except(componentCollectionB, new ComponentComparer());
(written off of the top of my head; may require minor modifications to get it to compile.)
LINQ will allow you to find the GUIDs you need, but LINQ sequences are generally immutable; you'll still need to use some kind of loop to actually change the collection. The trick is getting the correct instances of your original collection that you want to remove.
Implementing one of the equality/comparison interfaces is one way to go, and if you need to compare your objects for equality in multiple places, is definitely the way to go. If you don't want to do that, this should get you what you want:
var removeme = (from x in this.ComponentInformationCollection
join y in component on x.ID equals y.ID
select x).ToList();
removeme.ForEach(x => this.ComponentInformationCollection.Remove(x));
Thinking out loud (meaning I didn't create a project and types and compile this), but how about:
var cancelledComponents = component.Select(c=> new ComponentInformation() {ID = c.ID}).ToList();
cancelledComponents.ForEach(c => ComponentInformationCollection.Remove(c));
There are a number of ways to solve this... this is a pretty simple Linq statement to query the ones you are looking for from the collection.
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
Here is the little test app I put together to demo it.
class Program
{
static void Main(string[] args)
{
List<TypeA> typeAList = new List<TypeA>();
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
List<TypeB> typeBList = new List<TypeB>();
typeBList.Add(new TypeB() { ID = typeAList[0].ID });
typeBList.Add(new TypeB() { ID = typeAList[1].ID });
//this is the statement
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
}
}
class TypeA
{
public Guid ID { get; set; }
}
class TypeB
{
public Guid ID { get; set; }
}

Categories