HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Reduce or improve Linq query with nested from-where clauses

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linqwithquerywhereimprovenestedreducefromclauses

Problem

I am new to linq to objects in C#. I have the following query which works adequately. Is there someway to reduce or rewrite this query using joins or something else?

var query =
        from container in containers
        let items = container.GetItems()
            from itemA in items
            let tag = CreateTag(itemA)
            where IsInteresting(tag)
                from itemB in items
                let derivedTag = CreateDerivedTag(tag)
                where CreateTag(itemB) == derivedTag
                select new { first = itemA, second = itemB };


To summarize,

  • there are a set of item sequences



-
for each item sequench

  • for each item A that is IsInteresting()



  • find an item B in the same sequence that matches based on the criteria derived from the item A



  • create a new item containing a reference to matching items A and B



-
return a collection of all such matching items

Solution

After a lot of setup, I arrived at this:

var extensionMethodQuery =
    from container in containers
    let items = container.GetItems()
    select items.GetItemDerivedTagItems
        (items.SelectInterestingItemTags()).FirstOrDefault();


Your original query looks pretty optimal, but it took a while to see why. I broke it down into its component parts, and concluded that I'd concentrate on making it more reusable and readable. Personally, I find lambda joins too verbose, so created two extension methods instead:

public static class Filters
{
    public static IEnumerable SelectInterestingItemTags
        (this IEnumerable items)
    {
        return
            from item in items
            let tag = Tag.CreateTag(item)
            where Tag.IsInteresting(tag)
            select new ItemTag(item, tag);
    }

    public static IEnumerable
        GetItemDerivedTagItems
        (this IEnumerable items, IEnumerable itemTags)
    {
        return
            from itemTag in itemTags
            from item in items
            let derivedTag = Tag.CreateDerivedTag(itemTag.tag)
            where Tag.CreateTag(item) == derivedTag
            select new ItemDerivedTagItem(itemTag.item, item);

    }
}


You'll notice I needed to create two extra classes to enable this, ItemTag and ItemDerivedTagItem, that replace the anonymous classes. Those names are probably awful, but not knowing the context, I did the best I could. The IsInteresting and CreateDerivedTag methods were made static member methods of their classes, too. The results were compared successfully via unit tests, against my dummy classes and dummy data, so hopefully you can verify that with yours.

As ever, the proof is in the profiling, and for me at least, the extension method query is 4 times faster. I can supply the dummy classes and data I used if needed, but as mentioned in the comments, I'd much prefer those supplied next time please!

Code Snippets

var extensionMethodQuery =
    from container in containers
    let items = container.GetItems()
    select items.GetItemDerivedTagItems
        (items.SelectInterestingItemTags()).FirstOrDefault();
public static class Filters
{
    public static IEnumerable<ItemTag> SelectInterestingItemTags
        (this IEnumerable<Item> items)
    {
        return
            from item in items
            let tag = Tag.CreateTag(item)
            where Tag.IsInteresting(tag)
            select new ItemTag(item, tag);
    }

    public static IEnumerable<ItemDerivedTagItem>
        GetItemDerivedTagItems
        (this IEnumerable<Item> items, IEnumerable<ItemTag> itemTags)
    {
        return
            from itemTag in itemTags
            from item in items
            let derivedTag = Tag.CreateDerivedTag(itemTag.tag)
            where Tag.CreateTag(item) == derivedTag
            select new ItemDerivedTagItem(itemTag.item, item);

    }
}

Context

StackExchange Code Review Q#41273, answer score: 4

Revisions (0)

No revisions yet.