LINQ provides a wide-range of query operators, many of which have been demonstrated in the previous sections. The purpose of this section is to succinctly summarize the complete set of LINQ query operators listed in Table 1. Recall that you must import the System.Query namespace to use these operators.
Aggregate
The Aggregate operator applies a function over a sequence, with or without an initial seed value. The result can be post-processed by another function is desired.
int[] ints = { 1, 2, 3, 4, 5, 6 };
var query = from ...;
int sum = ints.Aggregate( (a, b) => a + b );
int product = ints.Aggregate( 1, (a, b) => a * b );
int sump1 = ints.Aggregate( 0, (a, b) => a + b, r => r+1 );
var result = query.Aggregate(...);
Console.WriteLine(sum); // 21
Console.WriteLine(product); // 720
Console.WriteLine(sump1); // 22
Console.WriteLine(result);
The sequence can be of any type T.
See also: Average, Count, LongCount, Max, Min, and Sum.
Table 1. List of Standard LINQ Query Operators |
| ||
Operator | Lazy? | Description | |
Aggregate | No | Applies a function to a sequence, yielding a single value. | |
All | No | Applies a function to a sequence to see if all elements satisfy the function. | |
Any | No | Applies a function to a sequence to see if any element satisfies the function. | |
Average | No | Computes the average of a numeric sequence. | |
Cast | Yes | Yields the elements of a sequence type-casted to a given type. | |
Concat | Yes | Yields the concatenation of two sequences S1 and S2. | |
Contains | No | Searches a sequence to see if it contains a given element. | |
Count | No | Counts the number of elements in a sequence, yielding an integer result. | |
DefaultIfEmpty | Yes | Given a sequence S, yields S or a sequence with the default value if S is empty. | |
Distinct | Yes | Returns a sequence with duplicates eliminated. | |
ElementAt | No | Returns the ith element of a sequence. | |
ElementAtOrDefault | No | Returns the ith element of a sequence, or the default value if sequence is empty. | |
Empty | Yes | Yields an empty sequence. | |
EqualAll | No | Compares two sequences for equality. | |
Except | Yes | Given two sequences S1 and S2, returns the set difference S1 S2. | |
First | No | Returns the first element of a sequence. | |
FirstOrDefault | No | Returns the first element of a sequence, or the default value if sequence is empty. | |
Fold | No | Obsolete, see Aggregate. | |
GroupBy | Yes | Groups the elements of a sequence by key. | |
GroupJoin | Yes | Performs a join of two sequences S1 and S2, yielding a hierarchical result. | |
Intersect | Yes | Given two sequences S1 and S2, returns the set intersection of S1 and S2. | |
Join | Yes | Performs a traditional inner equijoin of two sequences S1 and S2. | |
Last | No | Returns the last element of a sequence. | |
LastOrDefault | No | Returns the last element of a sequence, or the default value if sequence is empty. | |
LongCount | No | Counts the number of elements in a sequence, yielding a long result. | |
Max | No | Returns the maximum of a sequence. | |
Min | No | Returns the minimum of a sequence. | |
OfType | Yes | Yields the elements of a sequence that match a given type. | |
OrderBy | Yes | Orders a sequence of elements by key into ascending order. | |
OrderByDescending | Yes | Orders a sequence of elements by key into descending order. | |
Range | Yes | Yields a sequence of integers in a given range. | |
Repeat | Yes | Yields a sequence of values by repeating a given value n times. | |
Reverse | Yes | Reverses the elements of a sequence. | |
Select | Yes | Applies a projection function to a sequence, yielding a new sequence. | |
SelectMany | Yes | Applies a projection function to flatten a sequence of sequences. | |
Single | No | Returns the lone element of a singleton sequence. | |
SingleOrDefault | No | Returns the lone element of a singleton sequence, or default if sequence is empty. | |
Skip | Yes | Skips the first n elements of a sequence, yielding the remaining elements. | |
SkipWhile | Yes | Given function F and sequence S, skips the initial elements of S where F is true. | |
Sum | No | Computes the sum of a numeric sequence. | |
Take | Yes | Yields the first n elements of a sequence. | |
TakeWhile | Yes | Given function F and sequence S, yields the initial elements of S where F is true. | |
ThenBy | Yes | Takes an ordered sequence and yields a secondary, ascending ordering. | |
TheyByDescending | Yes | Takes an ordered sequence and yields a secondary, descending ordering. | |
ToArray | No | Iterates across a sequence, capturing results in an array. | |
ToDictionary | No | Iterates across a sequence, capturing results in a Dictionary<K, V>. | |
ToList | No | Iterates across a sequence, capturing results in a List<T>. | |
ToLookup | No | Iterates across a sequence, capturing results in a Lookup<K, IEnumerable<V>>. | |
ToSequence | Yes | Casts a sequence as an IEnumerable sequence for use with standard query ops. | |
Union | Yes | Given two sequences S1 and S2, returns the set union of S1 and S2. | |
Where | Yes | Applies a Boolean function to a sequence, yielding a sub-sequence. |
6.1.2. All
The All operator applies a function over a sequence, checking to see if all of the elements satisfy the function, i.e., cause the function to return true. For example, do all the doctors have pager numbers? How about all the doctors retrieved by a particular query?
Doctors doctors = new Doctors();
var query = from doc in doctors ...;
bool allHavePagers = doctors.All(doc => doc.PagerNumber > 0);
bool theseHavePagers = query.All(doc => doc.PagerNumber > 0);
See also: Any, Contains, and EqualAll.
6.1.3. Any
The Any operator applies a function over a sequence, checking to see if any of the elements satisfy the function, i.e., cause the function to return true. For example, are there any doctors living in Lake Forest?
Doctors doctors = new Doctors();
bool inLakeForest = doctors.Any(doc => doc.City == "Lake Forest");
The function is optional; if omitted, the Any operator returns true if the sequence contains at least one element.
var query = from doc in doctors
where doc.City == "Lake Forest"
select doc;
bool inLakeForest = query.Any();
See also: All, Contains, and EqualAll.
6.1.4. Average
The Average operator computes the average of a sequence of numeric values. The values are either the sequence itself, or selected out of a sequence of objects.
int[] ints = { 1, 2, 3, 4, 5, 6 };
decimal?[] values = { 1, null, 2, null, 3, 4 };
Doctors doctors = new Doctors();
var query = from ...;
double avg1 = ints.Average();
decimal? avg2 = values.Average();
double avgYears = doctors.Average( doc =>
DateTime.Now.Subtract(doc.StartDate).Days / 365.25 );
var avg = query.Average();
Console.WriteLine(avg1); // 3.5
Console.WriteLine(avg2); // 2.5
Console.WriteLine(avgYears.ToString("0.00")); // 5.72
Console.WriteLine(avg);
The values can be of type int, int?, long, long?, decimal, decimal?, double, or double?. The resulting type is double, double?, double, double?, decimal, decimal?, double, or double?, respectively.
See also: Aggregate, Count, LongCount, Max, Min, and Sum.
6.1.5. Cast
The Cast operator yields the elements of a sequence type-casted to a given type T.
System.Collections.ArrayList al = new System.Collections.ArrayList();
al.Add("abc");
al.Add("def");
al.Add("ghi");
var strings = al.Cast<string>();
foreach(string s in strings) // "abc", "def", "ghi"
Console.WriteLine(s);
The Cast operator is commonly used to wrap pre-2.0 collections (such as ArrayLists) for use with LINQ. Here's a more interesting example of searching the Windows event log for all events logged by an application:
var entries = from entry in applog.Entries.Cast<System.Diagnostics.EventLogEntry>()
where entry.Source == "ApplicationName"
orderby entry.TimeWritten descending
select entry;
See also: OfType.
6.1.6. Concat
The Concat operator concatenates two sequences S1 and S2, yielding the elements of S1 followed by the elements of S2.
int[] ints1 = { 1, 2, 3 };
int[] ints2 = { 4, 5, 6 };
var query1 = from ...;
var query2 = from ...;
var all = ints1.Concat(ints2);
var results = query1.Concat(query2);
foreach(var x in all) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(var r in results)
Console.WriteLine(r);
The sequences may contain elements of any type T. This element type T must be the same at compile-time (e.g. object), but may differ at run-time:
object[] objects1 = { "abc", "def" };
object[] objects2 = { 1, 2, 3 };
var result = objects1.Concat(objects2);
See also: Union.
6.1.7. Contains
The Contains operator searches a sequence to see if it contains a given element. For example, is there a doctor with initials "gg" still working at University Hospital? One approach is to create a Doctor object with the initials we are looking for, and see if the collection contains this object:
Doctors doctors = new Doctors();
bool docExists = doctors.Contains( new Doctor("gg", ...) );
This assumes the Doctor class defines Equals based on a doctor's initials. Another approach (without this assumption) is to select all the initials and then see if the result contains the string "gg":
var query = from doc in doctors
select doc.Initials
bool docExists = query.Contains("gg");
See also: All, Any, EqualAll, and Where.
6.1.8. Count
The Count operator counts the number of elements in a sequence, yielding an integer result. The elements are either the sequence itself, or selected from a sequence of objects.
int[] ints = { 1, 2, 3, 4, 5, 6 };
decimal?[] values = { 1, null, 2, null, 3 };
IEnumerable<Doctor> doctors = new Doctors();
var query = from ...;
int count1 = ints.Count();
int count2 = values.Count();
int count3 = doctors.Count();
int count4 = doctors.Count( doc => doc.City == "Chicago" );
int count = query.Count();
Console.WriteLine(count1); // 6
Console.WriteLine(count2); // 5
Console.WriteLine(count3); // 12
Console.WriteLine(count4); // 5
Console.WriteLine(count);
The sequence can be of any type T.
See also: Aggregate, Average, LongCount, Max, Min, and Sum.
6.1.9. DefaultIfEmpty
Given a non-empty sequence, the DefaultIfEmpty operator yields this same sequence. If the sequence is empty, DefaultIfEmpty yields a sequence containing a single default value.
int[] ints1 = { 1, 2, 3, 4, 5, 6 };
int[] ints2 = { };
var query = from ...;
var ints = ints1.DefaultIfEmpty();
var zero = ints2.DefaultIfEmpty();
var minus1 = ints2.DefaultIfEmpty(-1);
var result = query.DefaultIfEmpty();
foreach(int x in ints) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(int x in zero) // 0
Console.WriteLine(x);
foreach(int x in minus1) // -1
Console.WriteLine(x);
foreach(var r in result)
Console.WriteLine(r);
The sequence can be of any type T; if the sequence is empty and a default value is not provided, the default value for type T is used.
See also: FirstOrDefault, GroupJoin, LastOrDefault, SingleOrDefault, and ElementAtOrDefault.
6.1.10. Distinct
Given a sequence of elements, the Distinct operator returns the same sequence without duplicates.
int[] ints = { 1, 2, 2, 3, 2, 3, 4 };
var query = from ...;
var distinctInts = ints.Distinct();
var distinctResults = query.Distinct();
foreach(var x in distinctInts) // 1, 2, 3, 4
Console.WriteLine(x);
foreach(var r in distinctResults)
Console.WriteLine(r);
The sequence can be of any type T.
See also: Except, Intersect, and Union.
6.1.11. ElementAt
The ElementAt operator returns the ith element of a sequence; the sequence must be non-empty, and i is 0-based.
int[] ints = { 1, 2, 3, 4, 5, 6 };
Doctors doctors = new Doctors();
var query = from ...;
int third = ints.ElementAt(2);
Doctor doctor = doctors.ElementAt(2);
var result = query.ElementAt(i);
Console.WriteLine(third); // 3
Console.WriteLine(doctor.Initials); // 3rd doc in Chicago: ch
Console.WriteLine(result);
The sequence can be of any type T.
See also: ElementAtOrDefault.
6.1.12. ElementAtOrDefault
The ElementAtOrDefault operator returns the ith element of a possibly empty sequence; i is 0-based.
int[] ints1 = { 1, 2, 3, 4, 5, 6 };
int[] ints2 = { };
Doctors doctors = new Doctors();
var query = from ...;
int x1 = ints1.ElementAtOrDefault(2);
int x2 = ints1.ElementAtOrDefault(6);
int x3 = ints2.ElementAtOrDefault(0);
Doctor doc1 = doctors.ElementAtOrDefault(2);
Doctor doc2 = doctors.ElementAtOrDefault(-1);
var result = query.ElementAtOrDefault(i);
Console.WriteLine(x1); // 3
Console.WriteLine(x2); // 0
Console.WriteLine(x3); // 0
Console.WriteLine(doc1 == null); // False
Console.WriteLine(doc2 == null); // True
Console.WriteLine(result);
The sequence can be of any type T; if the sequence is empty or i is invalid, the default value for type T is returned.
See also: ElementAt.
6.1.13. Empty
The Empty operator yields an empty sequence of the given type.
var emptyDocs = System.Query.Sequence.Empty<Doctor>();
foreach(var doc in emptyDocs) // <none>
Console.WriteLine(doc);
This operator is helpful when you need an empty sequence for another operator or a method argument.
6.1.14. EqualAll
The EqualAll operator compares two sequences for equality. Two sequences are equal if they are of the same length, and contain the same sequence of elements.
var query1 = from ...;
var query2 = from ...;
bool equal = query1.EqualAll(query2);
The sequences may contain elements of any type T. This element type T must be the same at compile-time (e.g. object), but may differ at run-time:
object[] objects1 = { "abc", "def" };
object[] objects2 = { 1, 2, 3 };
bool isFalse = objects1.EqualAll(objects2); // false
The result in such cases is false.
See also: All, Any, and Contains.
6.1.15. Except
Given two sequences of elements S1 and S2, the Except operator returns the distinct elements of S1 not in S2. In other words, Except computes the set difference S1 S2.
int[] intsS1 = { 1, 2, 2, 3, 2, 3, 4, 5, 6 };
int[] intsS2 = { 1, 3, 6, 7 };
var query1 = from ...;
var query2 = from ...;
var diffInts = intsS1.Except(intsS2);
var diffResults = query1.Except(query2);
foreach(var x in diffInts) // 2, 4, 5
Console.WriteLine(x);
foreach(var r in diffResults)
Console.WriteLine(r);
The sequences may contain elements of any type T. This element type T must be the same at compile-time (e.g. object), but may differ at run-time:
object[] objects1 = { "abc", "def" };
object[] objects2 = { 1, 2, 3 };
var result = objects1.Except(objects2);
See also: Distinct, Intersect, and Union.
6.1.16. First
The First operator returns the first element of a sequence; the sequence must be non-empty.
int[] ints = { 1, 2, 3, 4, 5, 6 };
Doctors doctors = new Doctors();
var query = from ...;
int first = ints.First();
Doctor doctor = doctors.First(doc => doc.City == "Chicago");
var result = query.First();
Console.WriteLine(first); // 1
Console.WriteLine(doctor.Initials); // mbl
Console.WriteLine(result);
The sequence can be of any type T.
See also: FirstOrDefault.
6.1.17. FirstOrDefault
The FirstOrDefault operator returns the first element of a possibly empty sequence.
int[] ints1 = { 1, 2, 3, 4, 5, 6 };
int[] ints2 = { };
Doctors doctors = new Doctors();
var query = from ...;
int x1 = ints1.FirstOrDefault();
int x2 = ints2.FirstOrDefault();
Doctor doc1 = doctors.FirstOrDefault(doc => doc.City == "Chicago");
Doctor doc2 = doctors.FirstOrDefault(doc => doc.City == "Planet Mars");
var result = query.FirstOrDefault();
Console.WriteLine(x1); // 1
Console.WriteLine(x2); // 0
Console.WriteLine(doc1 == null); // False
Console.WriteLine(doc2 == null); // True
Console.WriteLine(result);
The sequence can be of any type T; if the sequence is empty, the default value for type T is returned.
See also: First.
6.1.18. Fold
The Fold operator is considered obsolete, see Aggregate.
6.1.19. GroupBy
The GroupBy operator groups the elements of a sequence by key; the keys are yielded by a function applied to each element. Each resulting group is an IEnumerable sequence of elements S with a key K.
Doctors doctors = new Doctors();
var groups = doctors.GroupBy(doc => doc.City);
foreach(var group in groups)
{
Console.WriteLine("{0}:", group.Key); // Chicago, Evanston, ...
foreach(var doc in group) // {mbl,jl,...}, {ch,cm,...}, ...
Console.WriteLine(" {0}", doc.Initials);
}
A second version allows you to project exactly what data to store in the group, such as only the doctor's initials:
Doctors doctors = new Doctors();
var groups2 = doctors.GroupBy(doc => doc.City, doc => doc.Initials);
foreach(var group in groups2)
{
Console.WriteLine("{0}:", group.Key); // Chicago, Evanston, ...
foreach(var initials in group) // {mbl,jl,...}, {ch,cm,...}, ...
Console.WriteLine(" {0}", initials);
}
Additional versions allow you to provide a comparer of type IEqualityComparer for comparing keys.
Use of the group by clause in a query expression translates into application of the GroupBy operator. For example, the following statements are equivalent:
var groups = doctors.GroupBy(doc => doc.City);
var groups = from doc in doctors
group doc by doc.City into g
select g;
var groups2 = doctors.GroupBy(doc => doc.City, doc => doc.Initials);
var groups2 = from doc in doctors
group doc.Initials by doc.City into g
select g;
See also: OrderBy.
6.1.20. GroupJoin
The GroupJoin operator performs a join of two sequences S1 and S2, based on the keys selected from S1's and S2's elements. The keys are yielded by functions applied to each element; a third function determines the data projected by the join.
Unlike an inner join which yields essentially a table of joined records, the result of a GroupJoin is hierarchical. For each element in S1, there's a possibly empty sub-sequence of matching elements from S2. For example, the following query joins Doctors and Calls via the doctors' initials to determine which doctors are working on what dates:
DataSets.SchedulingDocs ds = FillDataSet();
var working = ds.Doctors.GroupJoin( ds.Calls,
doc => doc.Initials,
call => call.Initials,
(doc,call) => new { doc.Initials, Calls=call }
);
foreach(var record in working)
{
Console.WriteLine("{0}:", record.Initials);
foreach(var call in record.Calls)
Console.WriteLine(" {0}", call.DateOfCall);
}
Here's the output:
ay:
11/2/2006 12:00:00 AM
bb:
ch:
cm:
jl:
10/2/2006 12:00:00 AM
11/1/2006 12:00:00 AM
.
.
.
Use of the join into clause in a query expression translates into application of the GroupJoin operator. For example, the following statements are equivalent:
var working = ds.Doctors.GroupJoin( ds.Calls,
doc => doc.Initials,
call => call.Initials,
(doc,call) => new { doc.Initials, Calls=call }
);
var working = from doc in ds.Doctors
join call in ds.Calls
on doc.Initials equals call.Initials
into j
select new { doc.Initials, Calls = j };
To perform a traditional left outer join (and thus flatten the result into a table), use the DefaultIfEmpty operator as follows:
var working = from doc in ds.Doctors
join call in ds.Calls
on doc.Initials equals call.Initials
into j
from r in j.DefaultIfEmpty()
select new { doc.Initials, Call = r };
foreach(var record in working)
Console.WriteLine("{0}: {1}",
record.Initials,
(record.Call == null) ? "" : record.Call.DateOfCall.ToString());
See also: Join.
6.1.21. Intersect
Given two sequences of elements S1 and S2, the Intersect operator returns the distinct elements of S1 that also appear in S2. In other words, Intersect computes the set intersection of S1 and S2.
int[] intsS1 = { 1, 2, 2, 3, 2, 3, 4, 5, 6, 8 };
int[] intsS2 = { 6, 1, 3, 6, 7 };
var query1 = from ...;
var query2 = from ...;
var commonInts = intsS1.Intersect(intsS2);
var commonResults = query1.Intersect(query2);
foreach(var x in commonInts) // 1, 3, 6
Console.WriteLine(x);
foreach(var r in commonResults)
Console.WriteLine(r);
The sequences may contain elements of any type T. This element type T must be the same at compile-time (e.g. object), but may differ at run-time:
object[] objects1 = { "abc", "def" };
object[] objects2 = { 1, 2, 3 };
var result = objects1.Intersect(objects2);
See also: Distinct, Except, and Union.
6.1.22. Join
The Join operator performs an inner equijoin of two sequences S1 and S2, based on the keys selected from S1's and S2's elements. The keys are yielded by functions applied to each element; a third function determines the data projected by the join. For example, the following query joins Doctors and Calls via the doctors' initials to determine which doctors are working on what dates:
DataSets.SchedulingDocs ds = FillDataSet();
var working = ds.Doctors.Join( ds.Calls,
doc => doc.Initials,
call => call.Initials,
(doc,call) => new { doc.Initials, call.DateOfCall }
);
foreach(var record in working)
Console.WriteLine(record);
The query yields pairs of the form (Initials, DateOfCall), created from the joined Doctor and Call elements. Here's the output:
{Initials=ay, DateOfCall=11/2/2006 12:00:00 AM}
{Initials=jl, DateOfCall=10/2/2006 12:00:00 AM}
{Initials=jl, DateOfCall=11/1/2006 12:00:00 AM}
.
.
.
Use of the join clause in a query expression translates into application of the Join operator. For example, the following statements are equivalent:
var working = ds.Doctors.Join( ds.Calls,
doc => doc.Initials,
call => call.Initials,
(doc,call) => new { doc.Initials, call.DateOfCall }
);
var working = from doc in ds.Doctors
join call in ds.Calls
on doc.Initials equals call.Initials
select new { doc.Initials, call.DateOfCall };
See also: GroupJoin.
6.1.23. Last
The Last operator returns the last element of a sequence; the sequence must be non-empty.
int[] ints = { 1, 2, 3, 4, 5, 6 };
Doctors doctors = new Doctors();
var query = from ...;
int last = ints.Last();
Doctor doctor = doctors.Last(doc => doc.City == "Chicago");
var result = query.Last();
Console.WriteLine(last); // 6
Console.WriteLine(doctor.Initials); // tm
Console.WriteLine(result);
The sequence can be of any type T.
See also: LastOrDefault.
6.1.24. LastOrDefault
The LastOrDefault operator returns the last element of a possibly empty sequence.
int[] ints1 = { 1, 2, 3, 4, 5, 6 };
int[] ints2 = { };
Doctors doctors = new Doctors();
var query = from ...;
int x1 = ints1.LastOrDefault();
int x2 = ints2.LastOrDefault();
Doctor doc1 = doctors.LastOrDefault(doc => doc.City == "Chicago");
Doctor doc2 = doctors.LastOrDefault(doc => doc.City == "Planet Mars");
var result = query.LastOrDefault();
Console.WriteLine(x1); // 6
Console.WriteLine(x2); // 0
Console.WriteLine(doc1 == null); // False
Console.WriteLine(doc2 == null); // True
Console.WriteLine(result);
The sequence can be of any type T; if the sequence is empty, the default value for type T is returned.
See also: Last.
6.1.25. LongCount
The LongCount operator counts the number of elements in a sequence, yielding a long result. The elements are either the sequence itself, or selected from a sequence of objects.
int[] ints = { 1, 2, 3, 4, 5, 6 };
decimal?[] values = { 1, null, 2, null, 3 };
IEnumerable<Doctor> doctors = new Doctors();
var query = from ...;
long count1 = ints.LongCount();
long count2 = values.LongCount();
long count3 = doctors.LongCount();
long count4 = doctors.LongCount( doc => doc.City == "Chicago" );
long count = query.LongCount();
Console.WriteLine(count1); // 6
Console.WriteLine(count2); // 5
Console.WriteLine(count3); // 12
Console.WriteLine(count4); // 5
Console.WriteLine(count);
The sequence can be of any type T.
See also: Aggregate, Average, Count, Max, Min, and Sum.
6.1.26. Max
The Max operator finds the maximum of a sequence of values. The values are either the sequence itself, or selected out of a sequence of objects, and must implement IComparable for comparison purposes.
int[] ints = { 1, 2, 3, 6, 5, 4 };
decimal?[] values = { 1, null, 4, null, 3, 2 };
Doctors doctors = new Doctors();
var query = from ...;
int max1 = ints.Max();
decimal? max2 = values.Max();
string maxInitials = doctors.Max( doc => doc.Initials );
double maxYears = doctors.Max( doc =>
DateTime.Now.Subtract(doc.StartDate).Days / 365.25 );
var max = query.Max();
Console.WriteLine(max1); // 6
Console.WriteLine(max2); // 4
Console.WriteLine(maxInitials); // "vj"
Console.WriteLine(maxYears.ToString("0.00")); // 11.43
Console.WriteLine(max);
The sequence can be of any type T; the resulting type is the same.
See also: Aggregate, Average, Count, LongCount, Min, and Sum.
6.1.27. Min
The Min operator finds the minimum of a sequence of values. The values are either the sequence itself, or selected out of a sequence of objects, and must implement IComparable for comparison purposes.
int[] ints = { 6, 2, 3, 1, 5, 4 };
decimal?[] values = { 4, null, 1, null, 3, 2 };
Doctors doctors = new Doctors();
var query = from ...;
int min1 = ints.Min();
decimal? min2 = values.Min();
string minInitials = doctors.Min( doc => doc.Initials );
double minYears = doctors.Min( doc =>
DateTime.Now.Subtract(doc.StartDate).Days / 365.25 );
var min = query.Min();
Console.WriteLine(min1); // 1
Console.WriteLine(min2); // 1
Console.WriteLine(minInitials); // "ay"
Console.WriteLine(minYears.ToString("0.00")); // 0.67
Console.WriteLine(min);
The sequence can be of any type T; the resulting type is the same.
See also: Aggregate, Average, Count, LongCount, Max, and Sum.
6.1.28. OfType
The OfType operator yields the elements of a sequence that match a given type T. In other words, OfType filters a sequence by type.
System.Collections.ArrayList al = new System.Collections.ArrayList();
al.Add(1);
al.Add("abc");
al.Add(2);
al.Add("def");
al.Add(3);
var strings = al.OfType<string>();
foreach(string s in strings) // "abc", "def"
Console.WriteLine(s);
The OfType operator is commonly used to (a) wrap pre-2.0 collections (such as ArrayLists) for use with LINQ and (b) filter mixed collections. Here's a more interesting example of searching a user's Outlook contacts with LINQ:
Outlook.MAPIFolder folder = this.ActiveExplorer().Session.
GetDefaultFolder( Outlook.OlDefaultFolders.olFolderContacts );
var contacts = from contact in folder.Items.OfType<Outlook.ContactItem>()
where .
. // search criteria, e.g. contact.Email1Address != null
.
select contact;
See also: Cast.
6.1.29. OrderBy
The OrderBy operator orders a sequence of elements into ascending order; the keys used to order the sequence are yielded by a function applied to each element.
int[] ints = {3, 1, 6, 4, 2, 5};
Doctors doctors = new Doctors();
var sorted = ints.OrderBy(x => x);
var docs = doctors.OrderBy(doc => doc.Initials);
foreach(var x in sorted) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(var doc in docs) // ay, bb, ..., vj
Console.WriteLine(doc.Initials);
A second version allows you to provide a comparer of type IComparer for comparing keys and thus controlling the ordering, e.g., when you want to compare strings in a case-insensitive manner:
var docs = doctors.OrderBy(doc => doc.Initials,
StringComparer.CurrentCultureIgnoreCase);
Use of the orderby clause in a query expression translates into application of the OrderBy operator. For example, the following statements are equivalent:
var docs = doctors.OrderBy(doc => doc.Initials);
var docs = from doc in doctors
orderby doc.Initials
select doc;
See also: OrderByDescending, ThenBy, and ThenByDescending.
6.1.30. OrderByDescending
The OrderByDescending operator orders a sequence of elements into descending order; the keys used to order the sequence are yielded by a function applied to each element.
int[] ints = {3, 1, 6, 4, 2, 5};
Doctors doctors = new Doctors();
var sorted = ints.OrderByDescending(x => x);
var docs = doctors.OrderByDescending(doc => doc.Initials);
foreach(var x in sorted) // 6, 5, 4, 3, 2, 1
Console.WriteLine(x);
foreach(var doc in docs) // vj, tm, ..., ay
Console.WriteLine(doc.Initials);
A second version allows you to provide a comparer of type IComparer for comparing keys and thus controlling the ordering, e.g., when you want to compare strings in a case-insensitive manner:
var docs = doctors.OrderByDescending(doc => doc.Initials,
StringComparer.CurrentCultureIgnoreCase);
Use of the orderby clause in a query expression with the descending keyword translates into application of the OrderByDescending operator. For example, the following statements are equivalent:
var docs = doctors.OrderByDescending(doc => doc.Initials);
var docs = from doc in doctors
orderby doc.Initials descending
select doc;
See also: OrderBy, ThenBy, and ThenByDescending.
6.1.31. Range
The Range operator yields a sequence of integers in the given range, inclusive.
var oneToTen = System.Query.Sequence.Range(1, 10);
foreach(var x in oneToTen) // 1, 2, 3, ..., 10
Console.WriteLine(x);
6.1.32. Repeat
The Repeat operator yields a sequence of values by repeating a given value n times.
var zeros = System.Query.Sequence.Repeat(0, 8);
var strings = System.Query.Sequence.Repeat("", n);
Console.WriteLine(zeros.Count()); // 8
Console.WriteLine(strings.Count()); // n
foreach(var zero in zeros) // 0, 0, ..., 0
Console.WriteLine(zero);
6.1.33. Reverse
The Reverse operator reverses the elements of a sequence.
int[] ints = {1, 2, 3, 4, 5, 6};
Doctors doctors = new Doctors();
var revInts = ints.Reverse();
var docs = from doc in doctors
orderby doc.Initials
select doc;
var revDocs = docs.Reverse();
foreach(var x in revInts) // 6, 5, 4, 3, 2, 1
Console.WriteLine(x);
foreach(var doc in revDocs) // vj, tm, ..., ay
Console.WriteLine(doc.Initials);
See also: OrderBy and OrderByDescending.
6.1.34. Select
The Select operator applies a projection function over a sequence of elements, yielding a sequence of possibly new elements.
int[] ints = {1, 2, 3, 4, 5, 6};
Doctors doctors = new Doctors();
var sameInts = ints.Select(x => x);
var chicago = doctors.Where(d => d.City == "Chicago").Select(d => d.Initials);
var names = doctors.Select(d => new {LN=d.FamilyLastName, FN=d.GivenFirstName});
foreach(var x in sameInts) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(var d in chicago) // mbl, jl, kl, py, tm
Console.WriteLine(d);
foreach(var n in names) // {LN=Larsen, FN=Marybeth}, ...
Console.WriteLine(n);
Use of the select clause in a query expression translates into application of the Select operator (except in trivial cases where the Select operator can be optimized away). For example, the following statements are equivalent:
var chicago = doctors.Where(d => d.City == "Chicago").Select(d => d.Initials);
var chicago = from d in doctors
where d.City == "Chicago"
select d.Initials;
A second version of the Select operator provides an element's 0-based index in the sequence along with the element itself, for example:
var sumLast3 = ints.Select( (x,index) => (index >= ints.Length-3) ? x : 0 ).Sum();
Console.WriteLine(sumLast3); // 15
See also: SelectMany and Where.
6.1.35. SelectMany
The SelectMany operator applies a projection function over a sequence of sequences, yielding a "flattened" sequence of possibly new elements. For example, turning an array of arrays into an array:
List<int[]> list = new List<int[]>();
int[] ints123 = {1, 2, 3};
int[] ints456 = {4, 5, 6};
list.Add(ints123);
list.Add(ints456);
var flat = list.SelectMany(x => x);
foreach (var x in list) // Int32[], Int32[]
Console.WriteLine(x);
foreach (var x in flat) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
The SelectMany operator is commonly used to flatten hierarchical data. For example, the following query yields the list of doctors working in October 2006, with sub-lists of the calls each is working:
DataSets.SchedulingDocs ds = FillDataSet();
var oct2006 = from d in ds.Doctors
join c in ds.Calls
on d.Initials equals c.Initials
where c.DateOfCall >= new DateTime(2006, 10, 1) &&
c.DateOfCall <= new DateTime(2006, 10, 31)
group c by d.Initials into g
select g;
foreach (var group in oct2006) // jl: 1, mbl: 2
{
Console.WriteLine("{0}: {1}", group.Key, group.Count());
foreach (var call in group)
Console.WriteLine(call.DateOfCall);
}
To flatten the hierarchy into just the list of calls:
var calls = oct2006.SelectMany(call => call);
foreach (var c in calls)
Console.WriteLine(c.DateOfCall);
Use of the select clause in a query expression on all but the initial from clause translates into application of the SelectMany operator. For example, here's another way to flatten the list of calls for doctors working in October 2006:
var calls = from d in ds.Doctors
from c in ds.Calls
where
d.Initials == c.Initials &&
c.DateOfCall >= new DateTime(2006, 10, 1) &&
c.DateOfCall <= new DateTime(2006, 10, 31)
select c;
This is translated into the following application of the SelectMany operator:
var calls = ds.Doctors.SelectMany(
d => ds.Calls.Where(c => d.Initials == c.Initials &&
c.DateOfCall >= new DateTime(2006, 10, 1) &&
c.DateOfCall <= new DateTime(2006, 10, 31)));
A second version of the SelectMany operator provides an element's 0-based index in the outer sequence along with the element itself.
6.1.36. Single
The Single operator returns the lone element of a sequence; the sequence must contain exactly one element.
int[] ints = { 3 };
Doctors doctors = new Doctors();
var query = from ...;
int lone = ints.Single();
Doctor doctor = doctors.Single(doc => doc.Initials == "mbl");
var result = query.Single();
Console.WriteLine(lone); // 3
Console.WriteLine(doctor.PagerNumber); // 52248
Console.WriteLine(result);
The sequence can be of any type T.
See also: SingleOrDefault.
6.1.37. SingleOrDefault
The SingleOrDefault operator returns the lone element of a sequence; the sequence must be empty or contain exactly one element.
int[] ints1 = { 3 };
int[] ints2 = { };
Doctors doctors = new Doctors();
var query = from ...;
int x1 = ints1.SingleOrDefault();
int x2 = ints2.SingleOrDefault();
Doctor doc1 = doctors.SingleOrDefault(doc => doc.Initials == "mbl");
Doctor doc2 = doctors.SingleOrDefault(doc => doc.Initials == "AAA");
var result = query.SingleOrDefault();
Console.WriteLine(x1); // 3
Console.WriteLine(x2); // 0
Console.WriteLine(doc1 == null); // False
Console.WriteLine(doc2 == null); // True
Console.WriteLine(result);
The sequence can be of any type T; if the sequence is empty, the default value for type T is returned.
See also: Single.
6.1.38. Skip
The Skip operator skips the first n elements of a sequence, yielding the remaining elements. If n <= 0 the result is the sequence itself; if n >= sequence's length the result is an empty sequence.
int[] ints = { 1, 2, 3, 4, 5, 6 };
var query = from ...;
var last3 = ints.Skip(3);
var bottom10 = query.Skip( query.Count() - 10 );
foreach(var x in last3) // 4, 5, 6
Console.WriteLine(x);
foreach(var r in bottom10)
Console.WriteLine(r);
The sequence can be of any type T.
See also: Take.
6.1.39. SkipWhile
Given a function F and a sequence S, the SkipWhile operator skips the first n elements of S where F returns true, yielding the remaining elements. Two versions of F are supported: accepting an element, or accepting an element along with its 0-based index in the sequence.
int[] ints = { 1, 2, 3, 4, 5, 6 };
var query = from ...;
var all = ints.SkipWhile(x => false);
var none = ints.SkipWhile(x => true);
var last3 = ints.SkipWhile( (x,i) => i < 3 );
var lastN = query.SkipWhile(result => ...);
foreach(var x in all) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(var x in none) // <none>
Console.WriteLine(x);
foreach(var x in last3) // 4, 5, 6
Console.WriteLine(x);
foreach(var r in lastN)
Console.WriteLine(r);
The sequence can be of any type T.
See also: TakeWhile and Where.
6.1.40. Sum
The Sum operator computes the sum of a sequence of numeric values. The values are either the sequence itself or selected out of a sequence of objects.
int[] ints = { 1, 2, 3, 4, 5, 6 };
decimal?[] values = { 1, null, 2, null, 3, 4 };
Doctors doctors = new Doctors();
var query = from ...;
int sum1 = ints.Sum();
decimal? sum2 = values.Sum();
double sumYears = doctors.Sum( doc =>
DateTime.Now.Subtract(doc.StartDate).Days / 365.25 );
var sum = query.Sum();
Console.WriteLine(sum1); // 21
Console.WriteLine(sum2); // 10
Console.WriteLine(sumYears.ToString("0.00")); // 68.61
Console.WriteLine(sum);
The values can be of type int, int?, long, long?, decimal, decimal?, double, or double?. The resulting type is the same.
See also: Aggregate, Average, Count, LongCount, Max, and Min.
6.1.41. Take
The Take operator yields the first n elements of a sequence. If n <= 0 the result is an empty sequence; if n >= sequence's length the result is the sequence itself.
int[] ints = { 1, 2, 3, 4, 5, 6 };
var query = from ...;
var first3 = ints.Take(3);
var top10 = query.Take(10);
foreach(var x in first3) // 1, 2, 3
Console.WriteLine(x);
foreach(var r in top10)
Console.WriteLine(r);
The sequence can be of any type T.
See also: Skip.
6.1.42. TakeWhile
Given a function F and a sequence S, the TakeWhile operator yields the first n elements of S where F returns true. Two versions of F are supported: accepting an element, or accepting an element along with its 0-based index in the sequence.
int[] ints = { 1, 2, 3, 4, 5, 6 };
var query = from ...;
var all = ints.TakeWhile(x => true);
var none = ints.TakeWhile(x => false);
var first3 = ints.TakeWhile( (x,i) => i < 3 );
var firstN = query.TakeWhile(result => ...);
foreach(var x in all) // 1, 2, 3, 4, 5, 6
Console.WriteLine(x);
foreach(var x in none) // <none>
Console.WriteLine(x);
foreach(var x in first3) // 1, 2, 3
Console.WriteLine(x);
foreach(var r in firstN)
Console.WriteLine(r);
The sequence can be of any type T.
See also: SkipWhile and Where.
6.1.43. ThenBy
The ThenBy operator takes an ordered sequence and yields a secondary, ascending ordering; the keys used for the secondary ordering are yielded by a function applied to each element. Ordered sequences are typically produced by OrderBy or OrderByDescending, but can also be produced by ThenBy and ThenByDescending to yield additional sub-orderings.
Doctors doctors = new Doctors();
var docs = doctors.OrderBy(doc => doc.City).ThenBy(doc => doc.Initials);
foreach(var doc in docs)
Console.WriteLine("{0}: {1}", doc.City, doc.Initials);
A second version allows you to provide a comparer of type IComparer for comparing keys and thus controlling the ordering, e.g., when you want to compare strings in a case-insensitive manner:
var docs = doctors.OrderBy(doc => doc.City)
.ThenBy(doc => doc.Initials,
StringComparer.CurrentCultureIgnoreCase);
Use of an orderby clause in a query expression with subsequent keys translates into application of the ThenBy operator. For example, the following are equivalent:
var docs = doctors.OrderBy(doc => doc.City).ThenBy(doc => doc.Initials);
var docs = from doc in doctors
orderby doc.City, doc.Initials
select doc;
See also: OrderBy, OrderByDescending, and ThenByDescending.
6.1.44. ThenByDescending
The ThenByDescending operator takes an ordered sequence and yields a secondary, descending ordering; the keys used for the secondary ordering are yielded by a function applied to each element. Ordered sequences are typically produced by OrderBy or OrderByDescending, but can also be produced by ThenBy and ThenByDescending to yield additional sub-orderings.
Doctors doctors = new Doctors();
var docs = doctors.OrderBy(doc => doc.City).ThenByDescending(doc => doc.Initials);
foreach(var doc in docs)
Console.WriteLine("{0}: {1}", doc.City, doc.Initials);
A second version allows you to provide a comparer of type IComparer for comparing keys and thus controlling the ordering, e.g., when you want to compare strings in a case-insensitive manner:
var docs = doctors.OrderBy(doc => doc.City)
.ThenByDescending(doc => doc.Initials,
StringComparer.CurrentCultureIgnoreCase);
Use of an orderby clause in a query expression with subsequent keys and the descending keyword translates into application of the ThenBy operator. For example, the following are equivalent:
var docs = doctors.OrderBy(doc => doc.City).ThenByDescending(doc => doc.Initials);
var docs = from doc in doctors
orderby doc.City, doc.Initials descending
select doc;
See also: OrderBy, OrderByDescending, and ThenBy.
6.1.45. ToArray
The ToArray operator iterates across a sequence of values, yielding an array containing these values. For example, doctors living in Chicago:
Doctors doctors = new Doctors();
var query = from doc in doctors
where doc.City == "Chicago"
select doc;
Doctor[] chicago = query.ToArray();
Since queries are lazily evaluated, ToArray is commonly used to execute a query and capture the results in a simple data structure.
See also: ToDictionary, ToList, ToLookup, and ToSequence.
6.1.46. ToDictionary
The ToDictionary operator iterates across a sequence of values, yielding a Dictionary<K, V> of (key, value) pairs. Each key must be unique, resulting in a one-to-one mapping of key to value. For example, storing doctors by their initials:
Doctors doctors = new Doctors();
var query = from doc in doctors
where doc.City == "Chicago"
select doc;
Dictionary<string, Doctor> chicago = query.ToDictionary(doc => doc.Initials);
foreach(var pair in chicago)
Console.WriteLine("{0}: {1}", pair.Key, pair.Value.PagerNumber);
Since queries are lazily evaluated, ToDictionary is commonly used to execute a query and capture the results in a data structure that supports efficient lookup by key. For example, finding a doctor via their initials:
Doctor mbl = chicago["mbl"];
Another version provides control over exactly what values are stored in the data structure, e.g., only the doctor's email address:
Dictionary<string, string> emails = doctors.ToDictionary(doc => doc.Initials,
doc => doc.EmailAddress);
Finally, additional versions of ToDictionary allow you to provide a comparer of type IEqualityComparer for comparing keys.
See also: ToArray, ToList, ToLookup, and ToSequence.
6.1.47. ToList
The ToList operator iterates across a sequence of values, yielding a List<T> containing these values. For example, doctors living in Chicago:
Doctors doctors = new Doctors();
var query = from doc in doctors
where doc.City == "Chicago"
select doc;
List<Doctor> chicago = query.ToList();
Since queries are lazily evaluated, ToList is commonly used to execute a query and capture the results in a flexible data structure.
See also: ToArray, ToDictionary, ToLookup, and ToSequence.
6.1.48. ToLookup
The ToLookup operator iterates across a sequence of values, yielding a Lookup<K, V> of (key, value) pairs. The keys do not need to be unique; values with the same key form a collection under that key, resulting in a one-to-many mapping of key to values. For example, grouping doctors by city:
Doctors doctors = new Doctors();
var query = from doc in doctors
select doc;
Lookup<string, Doctor> cities = query.ToLookup(doc => doc.City);
foreach(var pair in cities)
{
Console.WriteLine("{0}:", pair.Key); // city
foreach(var doc in pair) // doctors in that city
Console.WriteLine(" {0}", doc.Initials);
}
Since queries are lazily evaluated, ToLookup is commonly used to execute a query and capture the results in a data structure that supports efficient lookup by key. For example, finding the doctors living in Chicago:
IEnumerable<Doctor> docs = cities["Chicago"];
Another version provides control over exactly what values are stored in the data structure, e.g., only the doctor's initials:
Lookup<string, string> cities = doctors.ToLookup(doc => doc.City,
doc => doc.Initials);
Finally, additional versions of ToLookup allow you to provide a comparer of type IEqualityComparer for comparing keys.
See also: ToArray, ToDictionary, ToList, and ToSequence.
6.1.49. ToSequence
The ToSequence operator casts a sequence as a sequence, thereby hiding any public members of the original sequencein particular those that might conflict or compete with the standard query operators. Use this operator when you want to gain access to the standard LINQ query operators.
For example, the following use of the Count query operator fails to compile:
Doctors doctors = new Doctors();
int count = doctors.Count(doc => doc.City == "Chicago"); // ERROR!
It fails because the Doctors class provides a conflicting Count property (inherited from List<T>). The ToSequence operator provides a quick solution:
int count1 = doctors.Count;
int count2 = doctors.ToSequence().Count();
int count3 = doctors.ToSequence().Count( doc => doc.City == "Chicago" );
Console.WriteLine(count1); // 12
Console.WriteLine(count2); // 12
Console.WriteLine(count3); // 5
See also: ToArray, ToDictionary, ToList, and ToLookup.
6.1.50. Union
Given two sequences of elements S1 and S2, the Union operator returns the distinct elements of S1, followed by the distinct elements of S2 not in S1. In other words, Union computes the set union of S1 and S2.
int[] intsS1 = { 1, 2, 2, 3, 2, 3, 4, 6 };
int[] intsS2 = { 6, 1, 3, 5 };
var query1 = from ...;
var query2 = from ...;
var allInts = intsS1.Union(intsS2);
var allResults = query1.Union(query2);
foreach(var x in allInts) // 1, 2, 3, 4, 6, 5
Console.WriteLine(x);
foreach(var r in allResults)
Console.WriteLine(r);
The sequences may contain elements of any type T. This element type T must be the same at compile-time (e.g. object), but may differ at run-time:
object[] objects1 = { "abc", "def" };
object[] objects2 = { 1, 2, 3 };
var result = objects1.Union(objects2);
See also: Distinct, Except, and Intersect.
6.1.51. Where
The Where operator applies a function to a sequence of elements, yielding a sub-sequence of these elements.
int[] ints = {1, 2, 3, 4, 5, 6};
Doctors doctors = new Doctors();
var even = ints.Where( x => x % 2 == 0);
var chicago = doctors.Where( doc => doc.City == "Chicago" );
foreach(var x in even) // 2, 4, 6
Console.WriteLine(x);
foreach(var doc in chicago) // mbl, jl, kl, py, tm
Console.WriteLine(doc.Initials);
Use of the where clause in a query expression translates into application of the Where operator. For example, the following statements are equivalent:
var chicago = doctors.Where( doc => doc.City == "Chicago" );
var chicago = from doc in doctors
where doc.City == "Chicago"
select doc;
A second version of the Where operator provides an element's 0-based index in the sequence along with the element itself, for example:
var last3 = ints.Where( (x,index) => (index >= ints.Length-3) ? true : false );
foreach (var x in last3) // 4, 5, 6
Console.WriteLine(x);
See also: Select.
No comments:
Post a Comment
Post your comments here: