简体   繁体   中英

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.

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;

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();
}

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() .

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;
                                      }
                                 );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM