May 2006 - Posts
Got a match?Sometimes we don’t actually want to examine the elements returned by a query. In fact, we’d rather not spend the machine cycles to retrieve all the elements that match a particular query. Instead, the Any() and All() methods perform some of the work of a query and return Boolean values that tell you if there was a match.
This query determines if at least 1 of the elements in the source contains the string “ei”:
string[] words = { "believe", "relief", "receipt", "field" };
bool iAfterE = words.Any(w => w.Contains("ei"));If you can test a condition, you can use the results of that query as the predicate to a new select.
(huh?)
Well, the following query explains. Suppose I want to produce a collection of every product category where at least one product is out of stock. We’ll run a query of all the products, and group them by category. We’ll push the results of that query into a new query. In this second query, if any of the products are out of stock, it creates a new object where the first property is the category key, and the second property is the sequence of products (one of which is out of stock):
// Additional comments mine:
List<Product> products = GetProductList();
var productGroups =
from p in products // Query all the products
group p by p.Category into g // Group by category. And, examine the units in stock:
where g.Any(p => p.UnitsInStock == 0) // If there are products out of stock
select new { Category = g.Key, Products = g }; // Create a sequence of the entire group
Similar to “Any”, the LINQ sequence libraries contain “All”, which returns true if every source element matches the predicate. This simple version determines if every number in the sequence is odd:
int[] numbers = { 1, 11, 3, 19, 41, 65, 19 };
bool onlyOdd = numbers.All(n => n % 2 == 1);As with Any, you can use All to chain queries to each other and return the groups of products where all products have some items in stock:
List<Product> products = GetProductList();
var productGroups =
from p in products
group p by p.Category into g
where g.All(p => p.UnitsInStock > 0)
select new { Category = g.Key, Products = g };
Next, we’ll start counting and aggregating the results of queries. (There’s a lot more to learn)
Part 1The general query syntax
Part 2The one where I discuss Object and Collection Initializers
Part 3The one where I finish restriction operators
Part 4Beginning to discuss projections
Part 5Anonymous types and projections
Part 6Discussing indexed, filtered, and compound queries
Part 7Finishing up the projection items
Part 8Projection operators and extension methods
Part 9OrderBy, ThenBy, and Descending, oh my
Part 10Grouping operators, and building nested groups
Part 11Set Operations, You bet
Part 12Conversions: caching collections
Part 13Where you at item, wher you at?
Links from Carl Franklin's remote RSS presentationCarl Franklin put together a large page of resources about his talk on RSS, Syndication, and Podcasting.
The page includes all the sites he discussed, and a link to all the source code he mentioned.
Carl's big page of linksincludes the URL's he discussed, the sample code, and more
Finding elements by position (first, last, Nth)This next installment (the first examining the May CTP), looks at the Element operators, which are extension methods that return elements from a query based on its location in the result sequence.
The first Element operator sample (LINQ 58) shows how to extract just the first element using a query:
Product product12 = (
from p in products
where p.ProductID == 12
select p )
.First();
Incredibly simple: .First() returns the first element. As I’ve discussed earlier, the First() method is an extension method defined in Sequence.cs.
KEY POINT: LINQ queries produce a sequence, not a collection. That means this does not enumerate the entire collection, the enumeration stops when the First() method returns an element. That means you don’t retrieve all products, just the ones you ask about. First is much more useful when you combine it with a condition. The next sample retrieves the first string from an array that starts with an ‘o’:
string[] strings = { "zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine" };string startsWithO = strings.First(s => s[0] == 'o');
This is all well and good, but what happens if the element you seek isn’t found? If you use First(), it throws an exception. That may be bad. In those cases, use FirstOrDefault:
int[] numbers = {};
int firstNumOrDefault = numbers.FirstOrDefault();firstNumOrDefault will be assigned the value of 0. There are no numbers, so FirstOrDefault returns the default value (in this case, default(int)
FirstOrDefault is more useful when you use a predicate to find a particular element. The next query looks for a particular element, and if the element is not found, it returns a default new product?
List<Product> products = GetProductList();
Product product789 = products.FirstOrDefault
(p => p.ProductID == 789);
If there is a product with ID 789, that element is returned. Otherwise, it returns null.
Of course, there are many times you don’t necessarily want the first element. So, if you want the second element, you use the ElementAt() extension method:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int fourthLowNum = (
from n in numbers
where n > 5
select n )
.ElementAt(1); The 101 queries sample does not include the full catalog of related methods, but there are several other methods that perform related functionality:
Last()
Last(predicate)
LastOrDefault()
LastOrDefault(predicate)
ElementAt(predicate)
But, there’s more to life than queries, The LINQ libraries provide generation methods that fill sequences for you. The Range method generates a sequence of elements, starting with the nth element, and producing k elements. This statement produces the numbers 100 – 149:
var numbers =
from n in Sequence.Range(100, 50)
select new {Number = n, OddEven = n % 2 ==
1 ? "odd" : "even"};
Range() has two different version. One that is specialized for integers, which generates a list of numbers, simply relying on the order of numbers. The second version produces a sub-range from any arbitrary IEnumerable<T> source.
Finally, the Repeat() extension method returns the same element N times. (Note that if the element to be returned is a reference type, you get N references to the same actual storage, not N different copies of the same element.
var numbers = Sequence.Repeat(7, 10);
The above statement generates a sequence containing the number ‘7’ ten times.
That’s it for this entry. Later this week, I’ll return with quantifiers. Have fun.
Part 1The general query syntax
Part 2The one where I discuss Object and Collection Initializers
Part 3The one where I finish restriction operators
Part 4Beginning to discuss projections
Part 5Anonymous types and projections
Part 6Discussing indexed, filtered, and compound queries
Part 7Finishing up the projection items
Part 8Projection operators and extension methods
Part 9OrderBy, ThenBy, and Descending, Oh my
Part 10Grouping operators, and building nested groups
Part 11Set Operations, You bet.
Part 12Conversions: caching collections
Using LINQ with ASP.NETScott Guthrie posted an item on using LINQ with ASP.NET projects. Check it out.
Scott's PostUsing LINQ with ASP.NET, Part 1
Congratulations gang, it went extremely wellCongratulations to all the people that worked so hard to put together the Ann Arbor Day of .NET. John Hopkins and Josh Holmes did the organization, and many of the other local user group leaders did quite a bit to make the event run smoothly:
Greg Huber, Darrell Hawley, Jim Holmes (all the way from Dayton), Jason Follas, Dave Redding, and more.
What pleased me the most are the comments from some of the attendees that the content rivaled or even exceeded the bigger conferences. The kudos for that goes to Josh Holmes, who acted as program chair in scheduling all the speakers. In addition to the well-known speakers in our area, Josh highlighted some of the best and the brightest .NET experts you may not have known. And they were fantastic too.
One of the downsides of presenting at an event is that you don't get to enjoy the other content as much as you'd like. You're too busy concentrating on your own session to attend others. So, here from the people that attended. More reviews here:
Michael EatonA good review for all the session Michael attended
A new CTP is availableThe teams built a new CTP for LINQ.
Cool new additions:
. Some intellisense support.
. New Join operators
. Expression statements (as opposed to expression operators)
. Join and GroupJoin.
. Expression.Compiler (compile your expression trees for in memory execution).
Go get it...
The LINQ homepageIt's got a link to the new download
An overview of the LINQ featuresetFawcette has just published a set of articles on SQL Server 2005. In addition to my article on LINQ, local tech good guy
Jason Follas has an article on SQL CLR.
My article on LINQSee how the LINQ syntax, specifically DLinq and XLinq, can increase your productivity and reduce errors
Jason's article on SQL CLRLearn about SQLCLR, writing database objects in managed code, and configuring SQL Server to use code located within .NET assemblies
If the standard collections don't have what you need, what will you do?A friend forwarded this question for me:
"Say I have class that needs to hold a collection of classes called Location. There are two methods I'm considering.
The first is to create a Dictionary<int, Location> member. This will provide random access to the Locations in the collection using key value. But, this requires I add each Location using the key value in two places.
locations.Add(LocID, new Location(LocID, Name)
This method feels kinda sloppy.
The second option is to create a LocationCollection (my own class) member to which I add Location classes to a List<Location>. This will let me add a location supplying the ID only once and let me get a Location with an indexer (of my own code).
locations.Add(LocID, Name)
But, it feels like I'm not taking full advantage of generics in that generics are supposed to replace custom collection classes.
What are your thoughts?"
Well, there isn’t quite enough information to give the best answer. So, I’ll give a few answers based on some possible assumptions.
First, let’s consider the LocID is a primary key, and a Location is a class meant to handle communications with some persistent storage in a database. You could use the Dictionary<Location, object> and leave the values all as null. You’d need to have two routines on Location to make this work properly. First, Location.Equals() would need to check the LocID and only the LocID. Meaning that if two Location objects had the same key, they would be equal. Second, you’d need to right Location.GetHashCode() to return the hash code for the Location ID (integer field).
There are some big assumptions here though. Most types would use reference semantics to determine equality. That’s no longer true. Even worse, the Location type would not follow value semantics either. Only one of the two fields participates in the equality of two Location instances. You would encounter strange problems if your entire application did not take this behavior into account.
As a second option, you could at least localize the extra dependencies on the location ID when you add items to the dictionary. My bet is that you have quite a few places in the code where you create new locations and add them to the current collection. One simple function would help that situation.
Third, the list is not a bad idea, depending on what operations you need to optimize. Finding an element in a sorted list is O(ln N), finding that same element in a dictionary should be O(1). It depends on the ratio of adding elements to finding elements.
Finally, if you are going to use this kind of idiom often, you can build your own Set class, that uses a delegate to retrieve the correct property of the object for the key. Here’s a subset of what you need. Note that I didn't derive this from the Dictionary class. Yes, that would have saved some work re-creating the Dictionary interface. But, unfortunately, the Dictionary class methods are not virtual, so overriding them doesn't work the way you'd expect.
// This class defines the delegate that would
// be used with the class below.
public static class LocationKay
{
public static int GetKeyValue(Location src)
{
return src.LocID;
}
}
public class Set<T, V>
{
// Some method that retrieves the key property from the object:
public delegate T GetKey(V value);
// Store the delegate for this collection:
private GetKey gen;
// construct with the delegate that
// retrieves the key property
public Set(GetKey genFunc)
{
gen = genFunc;
}
// Internal storage for the collection:
private Dictionary<T, V> storage = new Dictionary<T, V>();
// One of the many methods you need to write:
public void Add(V value)
{
storage.Add(gen(value), value);
}
}
At the bottom of the past meetings pageJim Holmes, president of the Dayton developers group, was kind enough to invite me to come speak at his user group in Dayton. He recently posted all the materials on his past meetings page. Link below.
The Dayton Past meetings pageMy presentation is at the bottom