Jesse Roper
Jesse Roper

Reputation: 1309

Converting for-loop to a linq query

I like to think of myself as pretty good with LINQ, but every now and then I try to accomplish something that I just can't get to work. I'd like to convert a SPListItemCollection to a dictionary so I can use the key to look up a value without the need for a LINQ query each time:

var formsConfigItems = new Dictionary<string, string>();
foreach (SPListItem item in list.GetItems(query))
    formsConfigItems.Add(item.Title, (item["Value"] == null ? string.Empty : item["Value"].ToString()));

This works, but I was hoping to do it in a cleaner fashion, using LINQ. (not a big deal but I like to use LINQ over for-loops whenever possible, although it's the same thing behind the scenes.

I tried to do something like this:

var formsConfigItems = (from SPListItem i in list.GetItems(query)
                        select new { i.Title, i["Value"].ToString() }).ToDictionary<string, string>(k=>k.Key, k=>k.Value);

But that doesn't seem to work. If I try to use a lambda expression on list.GetItems(query), I'm not given the option to use .Where or any LINQ commands (which is weird because it is an SPListCollection)

Thanks in advance.

Upvotes: 1

Views: 3236

Answers (2)

Ani
Ani

Reputation: 113472

Try:

var formsConfigItems = list.GetItems(query)
                           .Cast<SPListItem>()
                           .ToDictionary(item => item.Title, 
                                         item => Convert.ToString(item["Value"]));

To answer your queries:

If I try to use a lambda expression on list.GetItems(query), I'm not given the option to use .Where or any linq commands (which is weird because it is an SPListCollection)

That's because SPListCollection is an "old-school" collection that implements IEnumerable but not IEnumerable<T>, so C# / LINQ (at compile-time anyway) can't tell what type of items it contains. The Cast<SPListItem>() call helps work around this issue - it turns an IEnumerable into an IEnumerable<T>, allowing the type-algebra to work out at compile-time . Your for loop doesn't have this issue since you explicitly specify the type of the loop variable - the compiler inserts a cast on your behalf for each item in the sequence.

I tried to do something like this (query expression). But that doesn't seem to work.

That's because you are not constructing the anonymous type instance correctly (property names can't be inferred for arbitrary expressions) and your lambda expression isn't quite right either (the property names you use don't match the property names of the anonymous type). Try this instead:

var formsConfigItems = (from SPListItem i in list.GetItems(query)
                        select new 
                        { 
                            i.Title, 
                            Value = Convert.ToString(i["Value"])
                        }).ToDictionary(a => a.Title, a => a.Value);

Upvotes: 7

Joe Enos
Joe Enos

Reputation: 40431

Ani's got the better solution IMO, but one other thing you're missing: Your LINQ statement is creating a collection of anonymous items, but you're not giving names to the properties in that anonymous class.

The k=>k.Key expression doesn't work, because it doesn't know what Key is - you've only defined Title (since you didn't give it a name, it borrowed the one from the object). The Value one can't be automatically figured out, so it would throw a compiler error.

To do it this way, you'd need to specifically declare the names:

new { Key = i.Title, Value = i["Value"].ToString() }

Upvotes: 2

Related Questions