Reputation: 24368
I have a collection of objects and am curious about the way you would separate them into two lists - one list will have everything of a specific type, the other will have the remainder. One way I thought of doing it is:
var typeXs = (from o in collectionOfThings where o.Type == "typeX" select o);
var notTypeXs = (from o in collectionOfThings where o.Type != "typeX" select o);
The other way would be to just loop over collectionOfThings and assign based on if/else.
Both ways are simple and readable, but I'm just wondering if there is an even slicker way?
Upvotes: 0
Views: 427
Reputation: 901
I would use the 'OfType' link expression, along these lines:
var typeXs = collectionOfThigs.OfType<typeX>();
var notTypeXs = collectionOfThings.Except(typeXs);
(Including the .Except from another answer). This does assume you have the type and not just the type string however.
Also, there may be a performance penalty for not making the decision just once (the if with the split add is probably the way to go if it matters), but unless performance is a problem, clarity is my preference.
Upvotes: 0
Reputation: 5853
If you don't want to go thru the list twice, then:
var collectionOfThings = new[]
{
new Thing { Id = 1, Type = "typeX" },
new Thing { Id = 2, Type = "typeY" },
new Thing { Id = 3, Type = "typeZ" },
new Thing { Id = 4, Type = "typeX" }
};
var query = (from thing in collectionOfThings
group thing by thing.Type == "typeX" into grouped
//orderby grouped.Key descending
select new
{
IsTypeX = grouped.Key,
Items = grouped.ToList()
}).ToList();
var typeXs = query.Find(x => x.IsTypeX).Items;
var notTypeXs = query.Find(x => !x.IsTypeX).Items;
Upvotes: 0
Reputation: 39846
This example should demonstrate what you're after:
class MyObject
{
public int n;
public string t;
}
Load up my original list:
List<MyObject> allObjects = new List<MyObject>() {
new MyObject() { n = 0, t = "x" },
new MyObject() { n = 1, t = "y" },
new MyObject() { n = 2, t = "x" },
new MyObject() { n = 3, t = "y" },
new MyObject() { n = 4, t = "x" }
};
Split out the types using:
var typeXs = allObjects.FindAll(i => i.t == "x");
var notTypeXs = allObjects.FindAll(i => i.t != "x");
Or
var typeXs = allObjects.Where(i => i.t == "x").ToList<MyObject>();
var notTypeXs = allObjects.Except(typeXs).ToList<MyObject>();
Alternatively you could use the List.ForEach method which only iterates once and therefore theoretically should outperform the other two options. Also, it doesn't require referencing the LINQ libraries which means it's .NET 2.0 safe.
var typeXs = new List<MyObject>();
var notTypeXs = new List<MyObject>();
allObjects.ForEach(i => (i.t == "x" ? typeXs : notTypeXs).Add(i));
Upvotes: 4
Reputation: 284836
I don't want to ejoptimilate all over you, but I do think you should consider only iterating once.
// You can optimize this by specifying reasonable initial capacities.
List<TypedO> typeXs = new List<TypedO>();
List<TypedO> notTypeXs = new List<TypedO>();
foreach(TypedO o in list)
{
(o.Type == "typeX" ? typeXs : notTypeXs).Add(o); // Yeah, I know.
}
(corrected)
Upvotes: 0
Reputation: 1454
You could rewrite the second part as
var notTypeXs = collectionOfThings.Except(typeXs);
Upvotes: 1