Click here to Skip to main content
15,868,164 members
Articles / Programming Languages / C#

C# Lectures - Lecture 11: LINQ to 0bjects Part 2. Nondeferred Operators

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
13 Sep 2016CPOL11 min read 9.4K   155   10  
Next article from the C# series. Continuation about LINQ operators for LINQ to objects

Full Lectures Set

Introduction

This is second article about LINQ to Objects and it is continuation of first article for this topic. Here I'll concentrate on reviewing  rest of operators that are available in LINQ and focus on Nondeferred Operators.

Nondeferred Operators

In previous article I reviewed Deferred Operators if you take a look on them, you'll see that all of them return IEnumerable<T> or OrderedSequence<T>. Nondeferred Operators in opposite to Deferred return data types that are not IEnumerable<T> or OrderedSequence<T>.

 

Let's review all Nondeferred operators available at the moment:

In most examples we will use following data structure on which we will call operators that are reviewed:

C#
//LINQ TO OBJECTS
string[] CarBrands = { "Mersedes", "Ford", "Lexus", "Toyota",
                       "Honda", "Hyunday", "BMW", "KIA", "Chevrolet",
                       "Tesla", "Lamborghini", "Ferrari", "Lincoln",
                        "Cadillac"};
  • ToArray - creates array of the same type as input sequence. Has following prototype:
    • public static T[] ToArray<T>(this IEnumerable<T> source); - this function takes an input sequence of type T and transforms it to array of type T.

     CODE:

Console.WriteLine("\n-----------------TOARRAY");
List<string> Names = new List<string>();
Names.Add("Sergey");
Names.Add("Alexander");
Names.Add("Maria");
Names.Add("John");
Names.Add("Bruce");
Names.Add("Emily");
Console.WriteLine("Names type = " + Names.GetType().ToString());
var newNames = Names.ToArray();
Console.WriteLine("newNames type = " + newNames.GetType().ToString());

    RESULT:

Image 1

  • ToList - converts input sequence of type to list of the same type. Has following prototype:
    • public static List<T> ToList<T>(this IEnumerable<T> source); - this function takes input sequence of type T and transforms it to list of type T.

     CODE:

C#
Console.WriteLine("\n-----------------TOLIST");
Console.WriteLine("CarBrands type = " + CarBrands.GetType().ToString());
var newCarBrands = CarBrands.ToList();
Console.WriteLine("newCarBrands type = " + newCarBrands.GetType().ToString());

     RESULT:

Image 2

  • ToDictionary - converts input sequence of type T to dictionary of type T with key K. There are 4 prototypes here:
    • public static Dictionary<K, T> ToDictionary<T, K>(this IEnumerable<T> source,Func<T, K> keySelector); - this function takes input sequence of type T and delegate -keySelector to extract key from each sequence element and use it as key in Dictionary that is returned. Returned Dictionary is of the same type as input sequence. In returned dictionary to compare keys default comparer is used EqualityComparer<K>.Default.
    • public static Dictionary<K, T> ToDictionary<T, K>(this IEnumerable<T> source,Func<T, K> keySelector,IEqualityComparer<K> comparer); - this prototype is exact as previous one, but it gives you ability to provide a comparer object that implements IEqualityComparer<K> interface to compare dictionary keys.
    • public static Dictionary<K, E> ToDictionary<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector); - this prototype is exact as first one, but it gives you ability to provide elementSelector delegate that will transform input element of sequence from type T to type E. Result dictionary will contain elements of type E with keys of type K.
    • public static Dictionary<K, E> ToDictionary<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer); - fourth prototype is the most wide one. This is combination of second and third options. Here you can implement your own comparer for keys in dictionary and implement elementSelector to convert input sequences elements to different type that will be stored in result dictionary.

     CODE:

C#
Console.WriteLine("\n-----------------TODICTIONARY");
Console.WriteLine("-----------------first version");
Dictionary<int, string> outDict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s));
foreach (var element in outDict)
{
    Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
}
Console.WriteLine("-----------------second version");
IntComparer comp = new IntComparer();
outDict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s),comp);
foreach (var element in outDict)
{
    Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
}
Console.WriteLine("-----------------third version");
Dictionary<int, int> dict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s),el => el.Length);
foreach (var element in dict)
{
    Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
}
Console.WriteLine("-----------------fourth version");
dict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s), el => el.Length,comp);
foreach (var element in dict)
{
    Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);

     RESULT:

Image 3

Image 4

  • ToLookup -  creates a lookup from input sequence. Has 4 prototypes as well:
    • public static ILookup<K, T> ToLookup<T, K>(this IEnumerable<T> source,Func<T, K> keySelector); - this function takes input sequence of type T and delegate -keySelector to extract key from each sequence element and use it as key in Lookup that is returned. Returned Dictionary is of the same type as input sequence. In returned dictionary to compare keys default comparer is used EqualityComparer<K>.Default.
    • public static ILookup<K, T> ToLookup<T, K>(this IEnumerable<T> source,Func<T, K> keySelector,IEqualityComparer<K> comparer); - this prototype is exact as previous one, but it gives you ability to provide a comparer object that implements IEqualityComparer<K> interface to compare lookup keys.
    • public static ILookup<K, E> ToLookup<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector); - this prototype is exact as first one, but it gives you ability to provide elementSelector delegate that will transform input element of sequence from type T to type E. Result lookup will contain elements of type E with keys of type K.
    • public static ILookup<K, E> ToLookup<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer); - fourth prototype is the most wide one. This is combination of second and third options. Here you can implement your own comparer for keys in lookup and implement elementSelector to convert input sequences elements to different type that will be stored in result lookup.

     CODE:

C#
Console.WriteLine("\n-----------------TOLOOKUP");
Console.WriteLine("-----------------first version");
ILookup<int, string> outLook = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s));
IEnumerable<string> car = outLook[4];
foreach (var str in car)
{
    Console.WriteLine("Fifth car is = " + str);
}
Console.WriteLine("-----------------second version");
outLook = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s),comp);
car = outLook[3];
foreach (var str in car)
{
    Console.WriteLine("Fourth car is = " + str);
}
Console.WriteLine("-----------------third version");
ILookup<int, int> outLook2 = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s),el => el.Length);
IEnumerable<int> carl = outLook2[4];
foreach (var i in carl)
{
    Console.WriteLine("Fifth car lengh is = " + i);
}
Console.WriteLine("-----------------fourth version");
outLook2 = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s), el => el.Length,comp);
carl = outLook2[3];
foreach (var i in carl)
{
    Console.WriteLine("Fourth car lengh is = " + i);
}

     RESULT:

Image 5

  • SequenceEqual - this operator determines if two input sequences are equal. Has two prototypes:
    • public static bool SequenceEqual<T>(this IEnumerable<T> first,IEnumerable<T> second); - this function compares all elements of input sequences using System.Object.Equals method. If all elements are equal it returns true.
    • public static bool SequenceEqual<T>(this IEnumerable<T> first,IEnumerable<T> second,IEqualityComparer<T> comparer); - this function is the same as the first one, but it allows you to define your own comparer for sequences elements.

     CODE:

C#
Console.WriteLine("\n-----------------SEQUENCEEQUAL");
Console.WriteLine("-----------------first version");
int[] seq1 = { 0, 5, 25 };
int[] seq2 = { 0, 5, 25 };
string[] Cars2 = { "Ford", "Acura"};
Console.WriteLine(CarBrands.SequenceEqual(Cars2));//prints false
Console.WriteLine(seq1.SequenceEqual(seq2));//prints true
Console.WriteLine("-----------------second version");
Console.WriteLine(seq1.SequenceEqual(seq2,comp));//prints true

     RESULT:

Image 6

  • First - returns first element of the sequence or the one that matches input predicate. If you call it on empty sequence it will raise exception System.InvalidOperationException. Has two prototypes:
    • public static T First<T>(this IEnumerable<T> source); - simply returns first element of the sequence
    • public static T First<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns first element from sequence that matches predicate

     CODE:

C#
Console.WriteLine("\n-----------------FIRST");
Console.WriteLine("-----------------first version");
Console.WriteLine(CarBrands.First());//prints Merseds
Console.WriteLine("-----------------second version");
Console.WriteLine(CarBrands.First(s=>s.StartsWith("BM")));//prints BMW

     RESULT:

Image 7

  • FirstOrDefault - returns first element of the sequence or the one that matches input predicate. It is similar to the First operator but if the sequence is empty it returns default(T). As you remember for all reference type default value is null. Has two prototypes:
    • public static T FirstOrDefault<T>(this IEnumerable<T> source); - simply returns first element of the sequence
    • public static T FirstOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns first element from sequence that matches predicate

     CODE:

C#
Console.WriteLine("\n-----------------FIRSTORDEFAULT");
Console.WriteLine("-----------------first version");
Console.WriteLine(CarBrands.FirstOrDefault());//prints Merseds
string [] seqstr = {};
try
{
    Console.WriteLine(seqstr.First());
}
catch (System.InvalidOperationException e)
{
    Console.WriteLine("Fisrt operator on empty sequence raises exception");
}
if(String.IsNullOrEmpty(seqstr.FirstOrDefault()))
{
    Console.WriteLine("We got default value of string = null");
}
Console.WriteLine("-----------------second version");
Console.WriteLine(CarBrands.FirstOrDefault(s => s.StartsWith("BM")));//prints BMW

     RESULT:

Image 8

  • Last - returns last element of the sequence or the one that matches input predicate. If you call it on empty sequence it will raise exception System.InvalidOperationException. Has two prototypes:
    • public static T Last<T>(this IEnumerable<T> source); - simply returns first element of the sequence
    • public static T Last<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns first element from sequence that matches predicate

    CODE:

C#
Console.WriteLine("\n-----------------LAST");
Console.WriteLine("-----------------first version");
Console.WriteLine(CarBrands.Last());//prints Cadillac
Console.WriteLine("-----------------second version");
Console.WriteLine(CarBrands.Last(s => s.StartsWith("L")));//prints Lincoln

    RESULT:

Image 9

  • LastOrDefault - returns last element of the sequence or the one that matches input predicate. It is similar to the Last operator but if the sequence is empty it returns default(T). As you remember for all reference type default value is null. Has two prototypes:
    • public static T LastOrDefault<T>(this IEnumerable<T> source); - simply returns last element of the sequence
    • public static T LastOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns last element from sequence that matches predicate

    CODE:

C#
Console.WriteLine("\n-----------------LASTORDEFAULT");
Console.WriteLine("-----------------first version");
Console.WriteLine(CarBrands.LastOrDefault());//prints Cadillac
try
{
    Console.WriteLine(seqstr.Last());
}
catch (System.InvalidOperationException e)
{
    Console.WriteLine("Last operator on empty sequence raises exception");
}
if (String.IsNullOrEmpty(seqstr.LastOrDefault()))
{
    Console.WriteLine("We got default value of string = null");
}
Console.WriteLine("-----------------second version");
Console.WriteLine(CarBrands.LastOrDefault(s => s.StartsWith("L")));//prints Lincoln

    RESULT:

Image 10

  • Single - returns the single element of input sequence that contains only one element or returns single element from. Has two prototypes:
    • public static T Single<T>(this IEnumerable<T> source); - simply returns last element of the sequence
    • public static T Single<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns last element from sequence that matches predicate

    CODE:

C#
Console.WriteLine("\n-----------------SINGLE");
Console.WriteLine("-----------------first version");
string[] singleElementSequence = { "single one"};
Console.WriteLine(singleElementSequence.Single());//prints - single one
Console.WriteLine("-----------------second version");
Console.WriteLine(CarBrands.Single(s => s.StartsWith("Lin")));//prints Lincoln

    RESULT:

Image 11

  • SingleOrDefault - similar to previous operator it returns the single element of input sequence that contains only one element or returns single element from. The only difference is the way how it behaves when search element is not found, in this case it returns default(T). Has two prototypes:
    • public static T SingleOrDefault<T>(this IEnumerable<T> source); - simply returns last element of the sequence
    • public static T SingleOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns last element from sequence that matches predicate

    CODE:

C#
Console.WriteLine("\n-----------------SINGLEORDEFAULT");
Console.WriteLine("-----------------first version");
string[] singleElementSequence2 = {};
if (String.IsNullOrEmpty(singleElementSequence2.SingleOrDefault()))
{
    Console.WriteLine("We got default value of string = null");
}
Console.WriteLine("-----------------second version");
if (String.IsNullOrEmpty(singleElementSequence2.SingleOrDefault(s=> s.StartsWith("strange string"))))
{
    Console.WriteLine("We again got default value of string = null");
}

    RESULT:

Image 12

  • ElementAt - returns the element of the source from specific position in sequence. Has two prototypes:
    • public static T ElementAt<T>(this IEnumerable<T> source,int index); - takes input index and return element on that position

    CODE:

C#
Console.WriteLine("\n-----------------ELEMENTAT");
Console.WriteLine("Element at position 2 is: " + CarBrands.ElementAt(2));//prints - Lexus

    RESULT:

Image 13

  • - similar to previous operator it returns the element from specific index of input sequence or returns single element from. The only difference is the way how it behaves when input index is wrong or input sequence is null, in this case it returns default(T). Has one prototypes:
    • public static T ElementAtOrDefault<T>(this IEnumerable<T> source,int index); - simply returns last element of the sequence

    CODE:

C#
Console.WriteLine("\n-----------------ELEMENTATORDEFAULT");
if (String.IsNullOrEmpty(CarBrands.ElementAtOrDefault(-1)))
{
    Console.WriteLine("We got default value of string = null");
}

    RESULT:

Image 14

  • Any - returns true if any element of the input sequence matches condition. Has two prototypes:
    • public static bool Any<T>(this IEnumerable<T> source); - this function returns true if at least any element present in sequence
    • public static bool Any<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns true if at least one element of input sequence causes predicate tor return true

    CODE:

C#
Console.WriteLine("\n-----------------ANY");
Console.WriteLine("-----------------first version");
string[] emptySequence = { };
if (!emptySequence.Any())
{
    Console.WriteLine("Input sequence is empty");
}
Console.WriteLine("Has CarBrands any elements? : " + CarBrands.Any());
Console.WriteLine("-----------------second version");
Console.WriteLine("Does car brands has something that starts with B: " + CarBrands.Any(s=> s.StartsWith("B")));

    RESULT:

Image 15

  • All - returns true if all elements of the input sequence matches condition. Has following prototype:
    • public static bool All<T>(this IEnumerable<T> source,Func<T, bool> predicate); - returns true if all elements of input sequence cause predicate tor return true

    CODE:

C#
Console.WriteLine("\n-----------------ALL");
Console.WriteLine("Do all elements of CarBrands has more than 3 symbols: " + CarBrands.All(s => {if(s.Length>3)return true;return false;}));
Console.WriteLine("Do all elements of CarBrands has more than 2 symbols: " + CarBrands.All(s => { if (s.Length > 2)return true; return false; }));

    RESULT:

Image 16

  • Contains - returns true if any of the elements in input sequence contains input value. Has following prototypes:
    • public static bool Contains<T>(this IEnumerable<T> source,T value); - returns true if any of the elements of input sequence matches input value
    • public static bool Contains<T>(this IEnumerable<T> source,T value, IEqualityComparer<T> comparer); - same as previous version but with ability to define your own comparer

    CODE:

C#
Console.WriteLine("\n-----------------Contains");
Console.WriteLine("-----------------first version");
Console.WriteLine("Does CarBrands contain BMW: " + CarBrands.Contains("BMW"));
int[] ints = { 0, 1, 2, 3, 4, 5 };
Console.WriteLine("-----------------second version");
Console.WriteLine("Does ints contain 5 with comparer: " + ints.Contains(5,comp));

    RESULT:

Image 17

  • Count - returns number of elements in input sequence:
    • public static int Count<T>(this IEnumerable<T> source);
    • public static int Count<T>(this IEnumerable<T> source,Func<T, bool> predicate); - if first prototype is pretty straightforward, then second is very interesting. It returns count of elements in input sequence that match specific predicate condition. This function I treat as one of the most convenient and useful in real life programming as it simplifies your life a lot.

    CODE:

C#
Console.WriteLine("\n-----------------Count");
Console.WriteLine("-----------------first version");
Console.WriteLine("CarBrands count: " + CarBrands.Count());
Console.WriteLine("-----------------second version");
Console.WriteLine("CarBrands count where length > 4: " + CarBrands.Count(s=> s.Length > 4));

    RESULT:

Image 18

  • LongCount - returns number of elements in input sequence as long. Similar to count and also has two prototypes:
    • public static long LongCount<T>(this IEnumerable<T> source);
    • public static long LongCount<T>(this IEnumerable<T> source,Func<T, bool> predicate);

    CODE:

C#
Console.WriteLine("\n-----------------LongCount");
Console.WriteLine("-----------------first version");
Console.WriteLine("CarBrands count: " + CarBrands.LongCount());
Console.WriteLine("-----------------second version");
Console.WriteLine("CarBrands count where length > 5: " + CarBrands.LongCount(s => s.Length > 5));

    RESULT:

   Image 19

  • Sum - returns sum of elements of input sequence that contains numeric values: int, long, double or decimal. Has following prototypes:
    • public static Numeric Sum(this IEnumerable<Numeric> source);
    • public static Numeric Sum<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - this function gives you ability to implement selector delegate that select only values you want and in this case you can call it on a sequence that is not Numeric

    CODE:

C#
Console.WriteLine("\n-----------------Sum");
double[] doubles = { 0.1, 1.2, 2.3, 3.5, 4.6, 5.3 };
Console.WriteLine("-----------------first version");
Console.WriteLine("Sum of doubles is: " + doubles.Sum());
Console.WriteLine("-----------------second version");
Console.WriteLine("Sum of chars in CarBrands is: " + CarBrands.Sum(s=> s.Length));

    RESULT:

Image 20

  • Min - returns minimal value from input sequence. Has following prototypes:
    • public static Numeric Min(this IEnumerable<Numeric> source); - simplest one for numerical sequences.
    • public static T Min<T>(this IEnumerable<T> source); - same as first but for not numerical types
    • public static Numeric Min<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - can be called on not numerical sequences but selector should take from that sequence some numerical field.
    • public static S Min<T, S>(this IEnumerable<T> source,Func<T, S> selector); - this function is like second but if gives you ability to use selector delegate

    CODE:

C#
Console.WriteLine("\n-----------------Min");
Console.WriteLine("-----------------first version");
Console.WriteLine("Min of doubles is: " + doubles.Min());
Console.WriteLine("-----------------second version");
Console.WriteLine("Min in CarBrands: " + CarBrands.Min());
Console.WriteLine("-----------------third version");
Console.WriteLine("Min in CarBrands by length is: " + CarBrands.Min(s => s.Length));
Console.WriteLine("-----------------fourth version");
Console.WriteLine("Min in CarBrands (we imagine this is class and return string value  for name from it) : " + CarBrands.Min(s => s.ToString()));

    RESULT:

Image 21

  • Max - returns maximal value from input sequence. Has following prototypes:
    • public static Numeric Max(this IEnumerable<Numeric> source); - simplest one for numerical sequences.
    • public static T Max<T>(this IEnumerable<T> source); - same as first but for not numerical types
    • public static Numeric Max<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - can be called on not numerical sequences but selector should take from that sequence some numerical field.
    • public static S Max<T, S>(this IEnumerable<T> source,Func<T, S> selector); - this function is like second but if gives you ability to use selector delegate

    CODE:

C#
Console.WriteLine("\n-----------------Max");
Console.WriteLine("-----------------first version");
Console.WriteLine("Mxn of doubles is: " + doubles.Max());
Console.WriteLine("-----------------second version");
Console.WriteLine("Max in CarBrands: " + CarBrands.Max());
Console.WriteLine("-----------------third version");
Console.WriteLine("Max in CarBrands by length is: " + CarBrands.Max(s => s.Length));
Console.WriteLine("-----------------fourth version");
Console.WriteLine("Max in CarBrands (we imagine this is class and return string value  for name from it) : " + CarBrands.Max(s => s.ToString()));

    RESULT:

Image 22

  • Average - returns average of elements of input sequence that contains numeric values: int, long, double or decimal. Has following prototypes:
    • public static Numeric Average(this IEnumerable<Numeric> source);
    • public static Numeric Average<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - this function gives you ability to implement selector delegate that select only values you want and in this case you can call it on a sequence that is not Numeric

    CODE:

C#
Console.WriteLine("\n-----------------Average");
Console.WriteLine("-----------------first version");
Console.WriteLine("Average of doubles is: " + doubles.Average());
Console.WriteLine("-----------------second version");
Console.WriteLine("Average of lengths in CarBrands is: " + CarBrands.Average(s => s.Length));

    RESULT:

Image 23

Conclusions

 You should be very careful when using nondeffered operators. Most of the work well in such called "good" way. Once you put wrong input to it or operator can't handle data properly it will raise exception. Before you decide to use some operator open its documentation and make sure you're aware about extreme cases and the way it handles problematic situations and which exceptions or return values are generated in those cases.

Sources

  1. Pro LINQ Language Integrated Query in C# 2010 Adam Freeman and Joseph C. Rattz, Jr.
  2. https://msdn.microsoft.com
  3. http://www.codeproject.com/Articles

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Ukraine Ukraine
Working on different projects and technologies from web to low level core programming, from scripting languages to C++. With all this stuff my personal opinion is that C#,.NET and Microsoft environment is the best thing programmer may have. Basing on it I prefer to publish only about my favorite technologies that are .NET and Azure now.

P.S. Looking for partnership and cooperation in outsourcing.

Comments and Discussions

 
-- There are no messages in this forum --