ReSharper 5.0’s LINQ Refactoring Continues to be Amazing!

 

In this post, we’ll take a straight forward procedure based set of code and convert it to LINQ using a ReSharper from JetBrains suggestion.   I’ve found that in general, when I do things with foreach syntax, there is often a better way in Linq to do this.  The better does not jump out at me sometimes, however with ReSharper, it is often a button click away.

The initial code is as follows:

int selectedIndex = 0;
foreach (var rec in codeCampStringDictionary)
{
    if (rec.Key == codeCampYear)
    {
        break;
    }
    selectedIndex++;
}

And, ReSharper is going to suggest turning it into:

int selectedIndex = codeCampStringDictionary.TakeWhile(rec => rec.Key != codeCampYear).Count();

Simply Amazing!  TakeWhile had not even occurred to me.

(All while getting the Silicon Valley Code Camp Site Ready for our V5 Event, October 9th and 10th, 2010)

So, to explain the problem a little better, on the Silicon Valley Code Camp web site (which, by the way is scheduled for October 9th and 10th, 2010 this year), we have an ASP.NET DropDownList in the upper right hand corner that the user can switch between different years. That is, say they want to see all the sessions in 2008’s code camp, they can simply change the dropdown and the site completely updates.  When they do this, we don’t want to re-read the data table that contains the code camp years, we simply want to process the data that has already been stored in the ViewState of the DropDownList and use that to figure out the key value of the table entry for the code camp year they selected.

image

OK, if you didn’t follow all that, it really doesn’t matter.  This blog post is simply about how clever ReSharper is and how much I enjoy what the tool does.  It’s nice to have a tool teach me things!

The complete commented code is below that includes some comments about how it actually works.  I’m looking forward to several people posting comments letting me know that there is even a simpler way to do this.   I love my job!

 

// Create a dictionary out of values in a DropDownList.  That is,
// the DropDownList has a Key Value which is it's primary key
// and a text value which is the Code Camp Year.
Dictionary<int, string> codeCampStringDictionary =
DropDownListCodeCampYearID.Items.Cast<ListItem>().
OrderBy(a => a.Value).
ToDictionary(itemdd => Convert.ToInt32(itemdd.Value),
itemdd => itemdd.Text);

// Then, loop through the values in the dictionary and figure out
// the offset of a particular value. That is, if there are 3 values
// in the list of 2008,2009,2010 which have primary keys of 3,4,5
// we want to know, that given codeCampYear is 4, then selectedIndex
// is the second value and should be 2.
int selectedIndex = codeCampStringDictionary.TakeWhile
(rec => rec.Key != codeCampYear).Count();

string dateString = Utils.GetCodeCampDateStringByCodeCampYearId(codeCampYear);
DropDownListCodeCampYearID.SelectedIndex = selectedIndex;
HeaderId1.Text = String.Format("Saturday and Sunday, {0}", dateString);

Let the comments roll!

About Peter Kellner

Peter is a software professional specializing in mobile and web technologies. He has also been a Microsoft MVP since 2007. In addition, he's a multi-course video author at Pluralsight. To read more about Peter Kellner and his experience click here. For information about how Peter Kellner might be able to help you with your project click here.

Follow me:


Comments

  1. Peter Kellner says:

    Actually, now that I look at it a little more, I think TakeWhile is valid because I’m only looking for one match and when I get that, I don’t continue looking (order does not matter)

  2. Peter Kellner says:

    Thanks for pointing that out SUDIPTA. I believe you are 100% correct on this. Seems like I was going the long way here. When I look at the code, I have no idea what i was trying to do (but I was impressed with the resharper refactor)

  3. The suggestion to use TakeWhile on a Dictionary is plain wrong unless the Dictionary implements IList. Dictionaries are not guaranteed to keep the entries in order; the basis on which TakeWhile operates

  4. James,
    Very nice! and thanks for the insight. I am surprised it adds so much overhead. I’m really glad to know that. If you still have the example, I’d be interested in what the actual numbers are.

  5. Actually James, I just looked at your blog and see you’ve got all the numbers in a very well written post. Never mind on last comment.

    http://geekswithblogs.net/BlackRabbitCoder/archive/2010/04/23/c-linq-vs-foreach—round-1.aspx

  6. James,
    Very nice! and thanks for the insight. I am surprised it adds so much overhead. I’m really glad to know that. If you still have the example, I’d be interested in what the actual numbers are.

  7. Interesting, Resharper chooses TakeWhile() which is actually very heavy. While I agree it’s much more elegant, it is about 90% slower than the foreach loop (when I measured it at least). This is because it has to update the state machine for each item it hits the yield return on, but really you only care about the count and not the item itself. So I played around and you can do it with an enclosure on a count variable on Any() which fits better (any stops on first occurence of the condition).

    It’s ugly as hell, to be sure, but it goes from 90% overhead downto about 30%.


    // a new method that uses linq but evaluates the count in a closure.
    public static int TakeWhileViaLinq2(IEnumerable list, int target)
    {
    int count = 0;
    list.Any(item =>
    {
    if(item == target)
    {
    return true;
    }

    ++count;
    return false;
    });
    return count;
    }

    This is probably beyond the means of Resharper to suggest and honestly it’s so ugly I don’t want it to 🙂 just wanted to point out that there are performance implications to consider. Probably indicates there should be a new LINQ type expression: int CountWhile(this IEnumerable, Predicate)

Follow

Get every new post delivered to your Inbox

Join other followers: