Max Bündchen
Max Bündchen

Reputation: 1362

Selecting subset of collection with Linq

In this sample how to get only Foos that have any active Bar and return only the active Bars in the collection?

One possible solution is something like this, but it requires recreate the Foo elements:

fooList
    .Where(f => f.Bars.Any(b => b.IsActive))
    .Select(f => new Foo()
    {
        Name = f.Name,
        Bars = f.Bars.Where(b => b.IsActive).ToList()
    });
public class Foo
{
    public string Name { get; set; }

    public ICollection<Bar> Bars { get; set; }
}

public class Bar
{
    public string Name { get; set; }

    public bool IsActive { get; set; }
}

Edit 1

The objective is to get only Foos with any active Bar and for these Foos, just the Bars that was active.

FooA
    Bar1 (Active)
    Bar2 (Inactive)
FooB
    Bar3 (Inactive)
    Bar4 (Inactive)
FooC
    Bar5 (Active)
    Bar6 (Active)

Disired result:

FooA
    Bar1 (Active)
FooC
    Bar5 (Active)
    Bar6 (Active)

As pointed theres many simple solutions, but I'm wondering if the Linq has any way to do this without retrieve all Bars and then drop the inactive in a loop-like after all Bars was retrieve in memory.

Upvotes: 1

Views: 5926

Answers (4)

cracker
cracker

Reputation: 4906

You need to use

Adding the Data to List

List<Foo> employees = new List<Foo>();
Foo employee = new Foo();
employee.Name = "Emp1";
employee.Bars = new List<Bar>();
employee.Bars.Add(new Bar { Name = "Alpesh", IsActive = true });
employee.Bars.Add(new Bar { Name = "Krunal", IsActive = true });
employee.Bars.Add(new Bar { Name = "Karthik", IsActive = false });
employee.Bars.Add(new Bar { Name = "Rakesh", IsActive = true });
employees.Add(employee);

Fetching the Active Data Only

List<Foo> newList = employees.Select(m => new Foo
        {
            Name = m.Name,
            Bars = m.Bars.Where(u => u.IsActive == true).ToList()
        }).ToList();
return newList;

Upvotes: 1

Clever Neologism
Clever Neologism

Reputation: 1332

You can process the Foo objects in the select clause. Lambdas don't have to be simple expressions, they can contain multiple statements.

var cleanedList = fooList.Where(f => f.Bars.Any(b => b.IsActive))
                         .Select(f => {
                                          f.Bars.RemoveAll(b => !b.IsActive); 
                                          return f;
                                      }
                                 );

Upvotes: 0

Mike Strobel
Mike Strobel

Reputation: 25623

If you want a list of the active Bar instances pulled from all Foo, but don't need to know which Foo instances they came from, this should work for you:

var activeBars = fooList.SelectMany(f => f.Bars)
                        .Where(b => b.IsActive)
                     // .Distinct() /* eliminate duplicates if you want */
                        .ToList();

The SelectMany operator concatenates the Bars from every Foo into a single sequence. The Where operator filters out just the active ones. ToList() forces a snapshot to be populated immediately.

This gives you a List<Bar> containing only the Bar instances where IsActive == true. If two or more Foo might contain the same Bar, and you want to eliminate those duplicates, then add a Distinct() operator before ToList().

Upvotes: 0

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101681

If you don't want to re-create Foo's then use a loop

foreach(var foo in fooList.Where(f => f.Bars.Any(b => b.IsActive)))
{
    foo.Bars = foo.Bars.Where(b => b.IsActive).ToList();
}

Upvotes: 0

Related Questions