Konrad S
Konrad S

Reputation: 33

Advanced LINQ filtering

I have problem with advanced filtering data using LINQ.

I'd like to get list of Plan classes with Details list where Arguments in Items Lists contains specific characters. Also the Items list should contains only this filtered elements.

My classes look like below:

class Plan
{
    public string Name { get; set; }
    public List<Detail> Details { get; set; }

    public Plan()
    {
        Details = new List<Detail>();
    }
}

class Detail
{
    public string Setting { get; set; }

    public List<Item> Items { get; set; }

    public Detail()
    {
        Items = new List<Item>();
    }
}

class Item
{
    public string Arguments { get; set; }
}

My current solution look like this, but I think it isn't the best option. I tried to write this code using Where and Any, but I've got Plans list where Items contains all items.

var filteredPlans = plans.Select(x =>
            new Plan
            {
                Name = x.Name,
                Details = x.Details.Select(y =>
                    new Detail
                    {
                        Setting = y.Setting,
                        Items = y.Items.Where(c => c.Arguments.Contains("...")).Select(z =>
                              new Item
                              {
                                  Arguments = z.Arguments
                              }).ToList()
                    }).ToList()
            });

How can I write this code using WHERE statement or What is the best solution to do that?

Also how can I get harvest difference using LINQ EXPECT based on Items List? e.g. plans: contains all plans with items, plans2: contains all plans with filtered items, and the plans3 should contains all plans with items which not belong to plans2.

Upvotes: 1

Views: 120

Answers (1)

Broom
Broom

Reputation: 596

Does this work for you?

First I limit to only the plans where any of their details contain any item that matches the filter.

Then I limit details for each plan to only those with any item that matches the filter

Then I limit items for each plan

    private List<Plan> FilteredPlans(List<Plan> plans, string filter)
    {
        List<Plan> filteredPlans = plans.Where(plan => plan.Details.Any(detail => detail.Items.Any(item => item.Arguments.Contains(filter)))).ToList();

        foreach (var plan in filteredPlans)
        {
            plan.Details = plan.Details.Where(detail => detail.Items.Any(item => item.Arguments.Contains(filter))).ToList();

            foreach (var detail in plan.Details)
            {
                detail.Items = detail.Items.Where(item => item.Arguments.Contains(filter)).ToList();
            }
        }

        return filteredPlans;
    }

Also, here's another version as a single statement, but I think it's far less readable. I essentially limit the items first and then work my way backwards only keeping containers that aren't empty

    private List<Plan> FilteredPlansWithSelect(List<Plan> plans, string filter)
    {
        List<Plan> filteredPlans = plans.Select(plan => 
        new Plan()
        {
            Name = plan.Name,
            Details = plan.Details.Select(detail =>
                new Detail()
                {
                    Setting = detail.Setting,
                    Items = detail.Items.Where(item => item.Arguments.Contains(filter)).ToList()
                }).Where(detail => detail.Items.Count > 0).ToList()
        }).Where(plan => plan.Details.Count > 0).ToList();


        return filteredPlans;
    }

Edited for grammer

Upvotes: 1

Related Questions