SPS Home    >   Dgreath    >   Linq

Language Integrated Queries (Linq)

IEnumerable<T>

Linq is a system of extension methods designed to act on any valid IEnumerable<T> objects to provide the functionality expressed below. There are two main categories of Linq epxressions. The first takes as an IEnumerable<T> as a source by extension, iterates through the payload, and returns an IEnumerable<T> result. The other takes an IEnumerable<T> as a source and returns a scalar value. Linq methods can be stacked as needed to attain the final result set. There are a few other expressions that serve utilty functions.

Linq expressions all have a source enumeration, generally referred to as 't' or 'T' and optionally a specified enumeration referred to as 'u' or 'U'.

The amazing power of Linq derives from it's use of lambda expressions which allow tremendous control over the configuration of each expression. The various types of lambda include: predicate, t specifier, u specifier, transform, and result. A lambda expression is no more than a shorthand notion for an anonymous function. Each type is described below:

Include using System.Linq; to access these methods.

Reference: System.Linq.Enumerable.cs

Predicate Expressions

Consider the lambda expressed as (t => t.Key == "somevalue") 
which is equivalent to function(obj t){t.Key == "somevalue"}

So, then, this lambda expression could expand into something like this:

private bool delegate(object t)
{
   if(t.Key == "somevalue") yield return t;
}

A predicate lamba expression always evaluates to either a true or false Boolean value.

TSelector or USelector Expressions

Consider the lambda expression t => t.id
which is equivalent to function(obj t){t.id}

Which expands to something like this:
private string delegate(object t)
{
   return eval(t.id);
}

Selector lambda expressions always evauate to a scalar value.

Transform Expressions

A transform expression performs some operation on the referenced values and returns the modified result..

Consider the lambda epxressionn t => t.value.ToLower()
which is the equivalent to: function(object t){t.value.ToLower()}.

or expanded:
private string delegate(object t)
{
   return t.value.ToLower();
}

Result Expressions

Result expressions take values from source ('t') and specified ('u') enumerations and combine them into a new object which becomes the definition of the result enumeration. Result expressions are necessary when you need to add a property to a collection, remove a property from a collection, or change the type of a property in the collection.

Consider the lambda (t, u) => new { OwnerName = t.Name, Pet = u.Name }
which is the equivalent to:
function(object t, object u){new { OwnerName = t.Name, Pet = u.Name }}

or expanded:
public object delegate(object t, object u)
{
   Object result = new Object();
   result.OwnerName = t.name;
   result.Pet = u.name;
   return result;
}

Syntactic Sugar:

Every Linq expression works in the normal functional construct function(arguments){behavior} where 'T' is always the first argument. This approach is often used inside of another method where the logic flows from right to left. This is ideal, for example, where your function is the RHS of an assignment statement.

More commonly, Linq expressions are "dot joined" on another object as an extension method where 'T' comes in "through the dot" in the form someobject.ExtensionMethod(). This approach is commonly used outside of a method to allow the logic to flow from left to right.

AsEnumerable

Enumerable.AsEnumerable(T)

.AsEnumerable()

Iterates through the source array, dictionary, list, or lookup passing each element through to the result to convert an array to an IEnumberable type. The functional opposite of ToArray(), ToDictionary(), ToList(), or ToLookp().

Pseudocode:
foreach(var item in items)
{
   yield return item;
}

Cast

Enumerable.Cast<Type>(T)

.Cast<type>()

Iterates through the source enumeration, casting each element to the result.

Pseudocode:
foreach(var item in items)
{
    yield return (type)item;
}

Concat

Enumerable.Concat(T,U)

.Concat(U)

Iterates through the source enumeration passing the results to the output, then iterates through the specified enumeration to contatentate those elements to the source.

Pseudocode:
foreach(var item in sourceItems)
{
   yield return item;
}
foreach(var item in specifiedItems)
{
   yield return item;
}

DefaultIfEmpty

Enumerable.DefaultIfEmpty(T)
Enumeable.DefaultIfEmpty(T, DefaultValue)

.DefaultIfEmpty()
.DefaultIfEmpty(DefaultValue)

Iterates through the source collection and if no elements are present (that is, the collection is empty), returns a standard default value (0 for value types, null for reference types) or, optionally, a specified default value for cases where either zero or null isn't appropriate.

Distinct

Enumerable.Distinct(T)

.Distinct()

Iterates through the source enumeration and removes all duplicate elements.

ElementAt

Enumerable.ElementAt(T, Index)

.ElementAt(Index)

ElementAt() checks to see if an element exists at the specified index location and, if so, returns that element. If not, an appropriate error is thrown.

Error(s): ArgumentOutOfRange

ElementAtOrDefault

Enumerable.ElementAtOrDefault(T, Index)

.ElementAtOrDefault(Index)

ElementAtOrDefault() checks to see if an element exists at the specified index location, and, if so, returns that element. If not, a default element is returned and no errors are thrown.

Empty

Enumerable.Empty(T)

.Empty()

Empty() returns an empty enumeration of the type.

Except

Enumerable.Except(T, U)

.Except(U)

Iterates through the 'U' enumeration writing each into a set. Then, interates through the 'T' enumeration removing all elements in the set that are present in the 'T' enumeration producing a result set of exceptions.  Opposite of Intersect().

First

Enumerable.First(T)
Enumerable.First(T, Predicate)

.First()
.First(Predicate)

First() has two versions. The first simply returns the first item in the enumeration. Alternatively, a predicate lambda expression can be provided. When the predicate is declared, the first element that meets it's criteria is returned.As a practical matter, either needs to operate on ordered data. If the enumeration is empty, an error is thrown. Functionally the opposite of Last().

Error(s): NoElements, NoMatch

FirstOrDefault

Enumerable.FirstOrDefault(T)
Enumerable.FirstOrDefault(T, Predicate)

.FirstOrDefault()
.FirstOrDefault(Predicate)

FirstOrDefault() has two versions. The first simply returns the first item in the enumeration. Alternatively, a predicate lambda expression can be provided. When the predicate is declared, the first element that meets it's criteria is returned.As a practical matter, either needs to operate on ordered data. If the enumeration is empty, an default value is returned of the source type. Functionally the opposite of LastOrDefault().

GroupBy

Enumerable.GroupBy(T, TSelector)
Enumerable.GroupBy(T, TSelector, Result)
Enumerable.GroupBy(T, TSelector, ESelector)
Enumerable.GroupBy(T, TSelector, ESelector, Result)

.GroupBy(TSelector)
.GroupBy(TSelector, Result)
.GroupBy(TSelector, ESelector)
.GroupBy(TSelector, ESelector, Result)

GroupBy comes in two versions and works with a single enumeration. The first version iterates through the 'T' enumeration and returns all elements that match the specification of the TSelector. The second version allow the elements to be specified.

Either version can optionally apply a Result Expression to the results in the event that you need to add or remove a property from the result enumeration or change the type of one.

The end result is a list of groups where each group has a list of elements.

A custom comparer can also be implemented.

GroupJoin

Enumerable.GroupJoin(T, U, TSelector, USelector, Result)

.GroupJoin(U, TSelector, USelector, Result)

The elements of the 'U' enumeration are loaded into a Lookup object with the value specified in the USelector as the Key. Then we iterate through the 'T' enumeration and return all elements from the Lookup object that meet the join conditions specified by TSelector. Result expression then applied to produce the result elements.  So, for example, a list of orders by customer can be produced. This is the equivalent to a SQL left join conceptually.

GroupJoin provides for the use of custom comparer.

Error(s): ArgumentNull(Inner), ArgumentNull(Outer), ArgumentNull(OuterKeySelector) ArgumentNull(InnerKeySelector), ArgumentNull(ResultSelector)

Intersect

Enumerable.Intersect(T, U)

.Intersect(U)

Iterates through the source enumeration writing each element into a set. Then iterates through the specified enumeration removing all elements from the set that are not present in the second enumeration producing a result set of elements that exist only in both. Functionally, the opposite of Except().

Join

Enumerable.Join(T, U, TSelector, USelector, Result)

.Join(U, TSelector, USelector, Result)

The 'U' enumeration is loaded into a Lookup object (similar to a dictionary) using the value in USelector as the key value. The 'T' enumeration is then iterated through using the value in TSelector to reference the related element in the lookup object by it's respective key. The Result can then access all properties in 'T' and the associated 'U' element to produce the Result.  This is the equivalent to a SQL inner join conceptually.

Join also has provision for a custom comparer.

Error(s): ArgumentNull(Inner), ArgumentNull(Outer), ArgumentNull(OuterKeySelector) ArgumentNull(InnerKeySelector), ArgumentNull(ResultSelector)

Last

Enumerable.Last(T)
Enumerable.Last(T, Predicate)

.Last()
.Last(Predicate)

Last() has two versions. The first simply returns the last item in the enumeration. Alternatively, a predicate lambda expression can be provided. When the predicate is declared, the last element that meets it's criteria is returned.As a practical matter, either needs to operate on ordered data.  If the enumeration is empty, an error is thrown. Functionally the opposite of First().

Error(s): NoElements, NoMatch

LastOrDefault

Enumerable.LastOrDefault(T)
Enumerable.LastOrDefault(T, Predicate)

.LastOrDefault()
.LastOrDefault(Predicate)

LastOrDefault() has two versions. The first simply returns the last item in the enumeration. Alternatively, a predicate lambda expression can be provided. When the predicate is declared, the last element that meets it's criteria is returned.As a practical matter, either needs to operate on ordered data. If the enumeration is empty, an default value is returned of the source type. Functionally the opposite of FirstOrDefault().

OfType

Enumerable.OfType<Type>(T)

.OfType<Type>()

Iterates through the source enumeration and returns all elements of the specified type.

OrderBy

Enumerable.OrderBy(T, TSelector)

.OrderBy(TSelector)

Iterates through the source enumeration and returns an ordered A>Z enumeration according to the sort criteria specified by the lambda expression. Use ThenBy or ThenByDescending to specify additional sorting criteria.

The predicate specifies which field to sort on.

OrderByDescending

Enumerable.OrderByDescending(T, TSelector)

.OrderByDescending(TSelector)

Iterates through the source enumeration and returns an ordered Z>A enumeration according to the sort criteria specified by the lamba expression. Use ThenBy or ThenByDescending to specify additional sorting criteria.

The predicate specifies which field to sort on.

Reverse

Enumerable.Reverse(T)

.Reverse()

Iterates through the 'T' enumeration witing each element into a set. Then iterates through the set from end to begining producing a result with the elements ordered in reverse.

Select

Enumerable.Select(T, Transform)
Enumerable.Select(T, Transform, Result)

.Select(Transform)
.Select(Transform, Result)

Iterate through the source enumertion and transform each element according to the Transform lamda expression. If you want to include an additional property to the collection, remove a property from the collection, or change the type of a property in the collection, the Result parameter is needed, otherwise, just the Transform is all that's needed.

Pseudocode:
int i = -1;
foreach(var item in items)
{
   i++;
   yield return transform(item, i);
}
The predicate will evaluate each item and transform it to the desired result.

Error(s): ArgumentNull(Source), ArgumentNull(Selector)

SelectMany

Enumerable.SelectMany(T, Transform)

.SelectMany(Transform)

Iterate through the 'T' enumeration and transform each element into a single collection according to the lambda expression.

Pseudocode:
int i = -1
foreach(var item in items)
{
   i++;
   foreach(var transformedItem in predicate(item, i))
   {
      yield return transformedItem;
   }
}
The predicate defines the transformation that is applied to each item to produce the transformedItem.

Error(s): ArgumentNull(Source), ArgumentNull(Selector)

Single

Enumerable.Single(T)
Enumerable.Single(T, Predicate)

.Single()
.Single(Predicate)

Single() has two versions.  The first one determines that the enumeration has exactly one element and no more. If so, it returns that element. If it is empty or has more than one element, an appropriate error is thrown. The second version accepts a predicate lambda expression to determine if the enumeration has exactly one element that matches that criteria. If so, the element is returned. If it is empty or has more than one element, an appropriate error is thrown.

Error(s): NoElements, MoreThanOneElement, NoMatch, MoreThanOneMatch

SingleOrDefault

Enumerable.SingleOrDefault(T)
Enumerable.SingleOrDefault(T, Predicate)

.SingleOrDefault()
.SingleOrDefault(Predicate)

SingleOrDefault() has two versions. The first one determines that the enumeration has exactly one element. If so, it returns that element. However, if the enumeration  is empty, a default element is returned.  If it has more than one element, an appropriate error is thrown.  The second version accepts a predicate lamba expression to determine if the enumeration has exactly on element that matches the criteria. If so, that element is returned. If no instances of the specified element are found, a default element is returned. If more than one instance of the specified element is found, an appropriate error is thrown.

Error(s): MoreThanOneElement, MoreThanOneMatch

Skip

Enumerable.Skip(T, Count)

.Skip(Count)

Iterates through the source enumeration, ignoring the number of elements specified by the count and returns the rest. Functionally, the opposite of Take().
Primarily used along with Take to implement paging e.g. Skip(pageIndex).Take(itemsPerPage)

Pseudocode:
foreach(var item in items)
{
   if(--count > 0) continue;
   yield return item;
}

Error(s): ArgumentNull(Source)

SkipWhile

Enumerable.SkipWhile(T, Predicate)

.SkipWhile(Predicate)

Iterates through the source enumeration and skips elements as long as the condition specified in the lambda expression evalutates to true, then returns the rest of the set. Functionally the opposite of TakeWhile().

Pseudocode:
int i = -1;
foreach(var item in items)
{
   i++;
   if(!predicate(item, i)) continue;
   yield return item;
}
 

Error(s): ArgumentNull(Source), Argument(Predicate)

Take

Enumerable.Take(T, Count)

.Take(Count)

Iterates through the source enumeration and returns the number of elements specified in count. Take is functionally the opposite of Skip().
Primarily used along with Skip to implement paging e.g. Skip(pageIndex).Take(itemsPerPage)

Pseduocode:
foreach(var item in items)
{
   yield return item;
   if(--count == 0) break;
}

Error(s): ArgumentNull(Source)

TakeWhile

Enumerable.TakeWhile(T, Predicate)

.TakeWhile(Predicate)

Iterates through the source enumeration and returns elements as long as the condition specified in the lamda expressions evaluates to true, stopping at the first occurrence of a false condition. Functionally the opposite of SkipWhile().

Pseduocode:
int i = -1;
foreach(var item in items)
{
   i++;
   if(!predicate(item, i)) break;
   yield return item;
}
 

Error(s): ArgumentNull(Source), ArgumentNull(Predicate)

ThenBy

Enumerable.ThenBy(T, TSelector)

.ThenBy(TSelector)

Iterates through the source enumeration and returns an ordered A>Z enumeration according to the sort criteria specified by the lambda expression. Follows OrderBy or OrderByDescending or other ThenBy or ThenByDescending to specify additional sorting criteria.

The predicate specifies which field to sort on.

ThenByDescending

Enumerable.ThenByDescending(T, TSelector)

.ThenByDescending(TSelector)

Iterates through the source enumeration and returns an ordered Z>A enumeration according to the sort criteria specified by the lambda expression. Follows OrderBy or OrderByDescending or other ThenBy or ThenByDescending to specify additional sorting criteria.

ToArray

Enumerable.ToArray(T)

.ToArray()

Iterates through the source enumeration and returns the result as an array. Functionally the opposite of AsEnumerable().

ToDictionary

Enumerable.ToDictionary(T)

.ToDictionary()

Iterates through the source enumeration and returns the result as a dictionary. Functionally the opposite of AsEnumerable().

ToList

Enumerable.ToList(T)

.ToList()

Iterates through the source enumeration and returns the results as a list. Functionally the opposite of AsEnumerable().

ToLookup

Enumerable.ToLookup(T)

.ToLookup()

Iterates through the source enumeration and returns the result as a lookup.Functionally the oppposite of AsEnumerable().

Union

Enumerable.Union(T, U)

.Union(U)

Iterates through the source enumeration and passes all elements through to the result, then passes through the specified enumeration and passes any element that is not present in the source enumeration, effectively merging two enumerations with no duplicates.

Where

Enumerable.Where(T, Predicate)

.Where(Predicate)

Iterate through the source enumeration and return all elements that match the lambda expression:

Pseudocode:
int i = -1;
foreach(var item in items)
{
   i++;
   if(predicate(item,i)) yield return item;
}
 

Error(s): ArgumentNull(Source), ArgumentNull(Predicate)

Zip

Enumerable.Zip(T, U, Transform)

.Zip(U, Transform)

Concurrently iterates through both the 'T' and 'U' enumerations, applying the specified transform to elements of both to produce a 'zipped up' result. Thus, the value of a property in 'T' could be added to the value of a property in 'U' and the result returned.


Aggregates

Linq also includes a number of aggregate methods also designed to act on any valid IEnumerable<T> object to provide the functionality expressed below. Each takes as an IEnumerable<T> as a source by extension, iterates through the payload, and returns a scalar result  Linq aggregate methods can be stacked as needed to attain the final result.

Reference: System.Linq.Enumerable.cs

All

Boolean

Enumerable.All(T, Predicate)

.All(Predicate)

Iterates through the source enumeration and returns true if all elements match the predicate, otherwise returns false.

Error(s): ArgumentNull(Source), ArguementNull(Predicate)

Any

Boolean

Enumerable.Any(T, Predicate)

.Any(Predicate)

Iterates through the source enumeration and returns true if one or more elements matching the predicate exist, otherwise returns false.

Error(s): ArgumentNull(Source), ArgumentNull(Predicate)

Aggregate

Enumerable.Aggregate(T, AggregateFunction)
Enumerable.Aggregate(T, Seed, AggregateFunction)
Enumerable.Aggregate(T, Seed, AggregateFunction, Result)

.Aggregate(AggregateFunction)
.Aggregate(Seed, AggregateFunction)
.Aggregate(Seed, AggregateFunction, Result)

Aggregate provides a means to accumulate. It exists in three basic variations. 'AggregateFunction' specifies the operation to be performed. The result of that operation is carried forward as the seed of the next iteration. 'Seed' allows a starting point to be preset into the method. When 'seed' is not specified, the starting point is determined by the first element of 'T'. 'Result' specifies the nature of what the returned elements will look like.

Aggregate is the mechanism used iternally by Average, Min, Max, and Sum.

Error(s): ArgumentNull(Source), ArgumentNull(Function), ArgumentNull(ResultSelector), NoElements

Average

Integer, Long, Float, Double, Decimal

Enumerable.Average(T)
Enumerable.Average(T, Transform)

.Average()
.Average(Transform)

Average() has two variations. The first iterates through the 'T' enumeration of any of these types and returns the average value of all elements. The second iterates through the 'T' enumeration, performs the specified transform and returns the average value of all transformed elements.

Error(s): ArgumentNull(Source), NoElements

Contains

Boolean

Enumerable.Contains(T, TElement)
Enumerable.Contains<Type>(T, TElement)

.Contains(Element)
.Contains<Type>(Element)

Iterates through the source enumeration and returns true if one or more elements are in it, false otherwise.  Type can be either inferred or specified.  "T" is an enumeration of TElements.

Error(s): ArgumentNull(Source)

Count

Integer (Int32)

Enumerable.Count(T)


.Count()
.Count(Predicate)

There are two versions of Count(). The first iterates through the 'T' enumeration and returns the count of elements present. The second iterates through the 'T' enumeration and returns the count of element present that match the predicate.

Error(s): ArgumentNull(Source), ArgumentNull(Predicate)

LongCount

Long (Int64)

Enumerable.LongCount(T)
Enumerable.LongCount(T, Predicate)

.LongCount()
.LongCount(Predicate)

There are two versions of LongCount(). The first iterates through the 'T' enumeration and returns the count of elements present. The second iterates through the 'T' enumeration and returns the count of element present that match the predicate.

Error(s): ArgumentNull(Source), ArgumentNull(Predicate)

Max

Integer, Long, Float, Double, Decimal

Enumerable.Max(T)
Enumerable.Max(T, Transform)

.Max()
.Max(Transform)

Max() has two variations. The first iterates through the 'T' enumeration of any of these types and returns the maximum value of all elements contained therein. The second iterates through the 'T' enumeration, performs the specified tranform, and returns the maximum transformed value of all elements. So, for example, you can multiply two properties and use the calculated result for the Max evaluation.

Error(s): ArgumentNull(Source), NoElements

Min

Integer, Long, Float, Double, Decimal

Enumerable.Min(T)
Enumerable.Min(T, Transform)

.Min()
.Min(Transform)

Min() has two variations. The first iterates through the 'T' enumeration of any of these types and returns the minimum value of all elements. The second iterates through the 'T' enumeration, performs the specified transform, and returns the minimum transformed value of all elements. Thus, you could add two properties and use the calculated result for the Min evaluation.

Error(s): ArgumentNull(Source), NoElements

Range

IEnumerable<int>

Enumerable.Range(Start, Count)

Range() returns an enumeration of integers starting at "Start" and running for the number of elements specified by "Count". Range can be used with Skip() and Take() to produce integer sequences.

Error(s): ArgumentOutOfRange

Repeat


Enumerable.Repeat<Type>(TElement, Count)

.Repeat(Element, Count)

Repeat() returns an enumeration consisting of a sequence of specified elements in the amount specified by Count. Type is an enumeration of TElements.

SequenceEqual

Boolean

.SequenceEqual(T, U)

.SequenceEqual(U)

Start with Boolean result set to true. Concurrently iterate through both 'T' and "U' enumerations, testing that the value of each element is equal in both. If not, set Boolean to false and exit the iteration. A true result indicates both enumerations share exactly the same elements in the same sequence. are equal.

Sum

Integer, Long, Float, Double, Decimal

Enumerable.Sum<Type>(T)
Enumerable.Sum<Type>(T, Transform)

.Sum<Type>()
.Sum<Type>(Transform)

Sum() has two variations. The first iterates through the 'T' enumeration of any of these types and returns the sum of all elements. The second iterates through the 'T' enumeration, performs the specifed transform, and returns the transformed sum of all elements.

Error(s): ArgumentNull(Source), NoElements