tags:

views:

117

answers:

6

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?

A: 
coll.GroupBy(o => o.Type == "TypeX");
Jimmy
I'm not sure what to do with the IEnumerable<IGrouping<bool, Thing>> that it returns
jayrdub
+1  A: 

You could rewrite the second part as

var notTypeXs = collectionOfThings.Except(typeXs);
Dries Van Hansewijck
I think this is the cleanest, most expressive way.
Carl Manaster
A: 

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)

Matthew Flaschen
Thanks, I had thought of that: "The other way would be to just loop over collectionOfThings and assign based on if/else."
jayrdub
Sorry if I was redundant. I just had to recommend this option, and strictly speaking I didn't use if or else. :)
Matthew Flaschen
+4  A: 

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));
BenAlabaster
The second example's probably the best, but good job giving all the options. +1
BobTheBuilder
I actually prefer the last example just for the fact that it's 2.0 safe...
BenAlabaster
and it only iterates the collection a single time, both of the other options actually have to iterate the collection twice, even though perhaps they are a little more elegantly written.
BenAlabaster
That's a good point I suppose
BobTheBuilder
I really like the last one.
Matthew Flaschen
The last one is my fav
jayrdub
A: 

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;
taoufik
A: 

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.

Colin Dabritz