patterncsharpMinor
Reduce or improve Linq query with nested from-where clauses
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?
To summarize,
-
for each item sequench
-
return a collection of all such matching items
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:
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:
You'll notice I needed to create two extra classes to enable this,
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!
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.