JeremyWeir
JeremyWeir

Reputation: 24368

Slickest way to put objects into separate lists based on a property value

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

Answers (6)

Colin Dabritz
Colin Dabritz

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

tofi9
tofi9

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

BenAlabaster
BenAlabaster

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

Matthew Flaschen
Matthew Flaschen

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

Dries Van Hansewijck
Dries Van Hansewijck

Reputation: 1454

You could rewrite the second part as

var notTypeXs = collectionOfThings.Except(typeXs);

Upvotes: 1

Jimmy
Jimmy

Reputation: 91492

coll.GroupBy(o => o.Type == "TypeX");

Upvotes: 0

Related Questions