8
\$\begingroup\$

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

asked Feb 9, 2014 at 6:14
\$\endgroup\$
2
  • 3
    \$\begingroup\$ It looks like join might be possible. It might be easier to answer this question if you posted a complete program: e.g. definitions of CreateTag, CreateDerivedTag, IsInteresting, GetItems, together with some sample data in containers. \$\endgroup\$ Commented Feb 10, 2014 at 0:01
  • \$\begingroup\$ I tried answering this in lambda syntax, but this query is awfully strange. The final select and where are really hard to convert, the way I was doing it. Someone else can probably do a far better job. \$\endgroup\$ Commented Feb 10, 2014 at 16:24

1 Answer 1

4
\$\begingroup\$

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

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!

answered Feb 14, 2014 at 0:00
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.