Sami
Sami

Reputation: 2110

LINQ select all items of all subcollections that contain a string

I'm using jqueryui autocomplete to assist user in an item selection. I'm having trouble selecting the correct items from the objects' subcollections.
Object structure (simplified) is

public class TargetType
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<SubCategory> SubCategories { get; set; }

    public TargetType()
    {
        SubCategories = new HashSet<SubCategory>();
    }
}

public class SubCategory
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<SubTargetType> SubTargetTypes { get; set; }

    public SubCategory()
    {
        SubTargetTypes = new HashSet<SubTargetType>();
    }
}

Currently I'm doing this with nested foreach loops, but is there a better way?
Current code:

List<SubTargetResponse> result = new List<SubTargetResponse>();
foreach (SubCategory sc in myTargetType.SubCategories)
{
    foreach (SubTargetType stt in sc.SubTargetTypes)
    {
        if (stt.Name.ToLower().Contains(type.ToLower()))
        {
            result.Add(new SubTargetResponse {
                Id = stt.Id,
                CategoryId = sc.Id,
                Name = stt.Name });
        }
    }
}

Upvotes: 6

Views: 2306

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205619

Actually there are 2 different questions.

LINQ select all items of all subcollections that contain a string

Solutions:

(A) LINQ syntax:

var result =
    (from sc in myTargetType.SubCategories
     from stt in sc.SubTargetTypes.Where(t => t.Name.ToLower().Contains(type.ToLower()))
     select new SubTargetResponse
     {
         Id = stt.Id,
         CategoryId = sc.Id,
         Name = stt.Name
     })
    .ToList();

(B) Method syntax:

var result =
    myTargetType.SubCategories.SelectMany(
        sc => sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower())),
        (sc, stt) => new SubTargetResponse
        {
             Id = stt.Id,
             CategoryId = sc.Id,
             Name = stt.Name
        })
    .ToList();

Currently I'm doing this with nested foreach loops, but is there a better way?

Well, it depends of what do you mean by "better". Compare your code with LINQ solutions and answer the question. I personally do not see LINQ being better in this case (except no curly braces and different indentation, but a lot of a hidden garbage), and what to say about the second LINQ version in this answer - if that's "better" than your code, I don't know where are we going.

Upvotes: 0

Arghya C
Arghya C

Reputation: 10068

You can do using Linq like this

var result = myTargetType.SubCategories
                         .SelectMany(sc => sc.SubTargetTypes)
                         .Where(stt => stt.Name.ToLower().Contains(type.ToLower()))
                         .Select(stt => new SubTargetResponse {
                                        Id = stt.Id,
                                        CategoryId = sc.Id,
                                        Name = stt.Name });

The above query doesn't work. The following should work, but I'd not recommend that as that'd not be faster or more readable.

var result = myTargetType.SubCategories
                         .Select(sc => new Tuple<int, IEnumerable<SubTargetType>>
                                            (sc.Id, 
                                             sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))))
                         .SelectMany(tpl => tpl.Item2.Select(stt => new SubTargetResponse {
                                        Id = stt.Id,
                                        CategoryId = tpl.Item1,
                                        Name = stt.Name }));

Upvotes: 2

Related Questions