3. Grouping by Composite Keys (More Than One Value)
To
group using more than one value as the key (often referred to as a
composite key), you specify the grouping selector clause as an
anonymous type. Any number of key values can be
specified in this anonymous type, and any element that contains
identical values will dutifully be co-located in a group.
Listing 3 demonstrates the simplicity of specifying multiple key values in the group by expression. In this case, the LastName and the State
fields are used for grouping, placing all contacts with the same last
name from the same state in a group. The Console output from this
example is shown in Output 2.
Listing 3. Anonymous types can be used to group by more than one value (composite key)—see Output 2
/* this sample uses the same data as we saw in Table 2-2, but i've added 2 Gottshall's (one from the same state and another out of that state), and 2 Gauwain's -
Firstname Lastname State ----------------------------- Barney Gottshall CA Mandy Gottshall CA *added Bernadette Gottshall WA *added Armando Valdes WA Adam Gauwain AK Chris Gauwain AK *added Anthony Gauwain CA *added Jeffery Deane CA Collin Zeeman FL Stewart Kagel WA Chance Lard WA Blaine Reifsteck TX Mack Kamph TX Ariel Hazelgrove OR */
var q = from c in Contact.SampleData() group c by new { c.LastName, c.State };
foreach (var grp in q) { Console.WriteLine("Group - {0}, {1} - count = {2}", grp.Key.LastName, grp.Key.State, grp.Count()); }
|
Output 2.
Group - Gottshall, CA - count = 2 Group - Gottshall, WA - count = 1 Group - Valdes, WA - count = 1 Group - Gauwain, AK - count = 2 Group - Gauwain, CA - count = 1 Group - Deane, CA - count = 1 Group - Zeeman, FL - count = 1 Group - Kagel, WA - count = 1 Group - Lard, WA - count = 1 Group - Reifsteck, TX - count = 1 Group - Kamph, TX - count = 1 Group - Hazelgrove, OR - count = 1
|
It is not essential to use an anonymous type as the
grouping key selector to achieve multiple-property groups. A named type
containing all of the properties participating in the key can be used
for the same purpose, although the method is more complex because the
type being constructed in the projection needs a custom override of the
methods GetHashCode and Equals to force comparison by property values rather than reference equality. Listing 4 demonstrates the mechanics of creating a class that supports composite keys using the fields LastName and State. The results are identical to that shown in Output 2.
Listing 4. Creating a composite join key using a normal class type
public class LastNameState { public string LastName { get; set; } public string State { get; set; }
// follow the MSDN guidelines - // http://msdn.microsoft.com/en-us/library/ // ms173147(VS.80).aspx public override bool Equals(object obj) { if (this != obj) { LastNameState item = obj as LastNameState; if (item == null) return false; if (State != item.State) return false; if (LastName != item.LastName) return false; }
return true; }
// follow the MSDN guidelines - // http://msdn.microsoft.com/en-us/library/ // system.object.gethashcode.aspx public override int GetHashCode() { int result = State != null ? State.GetHashCode() : 1; result = result ^ (LastName != null ? LastName.GetHashCode() : 2);
return result; } }
var q = from c in Contact.SampleData() group c by new LastNameState { LastName = c.LastName, State = c.State };
|