Digital Garden
Computer Science
C#
Lambdas and LINQ

Lambdas and LINQ

Lambdas

Lambda expressions are just another way to write methods.

static bool SomePredicate(Point p)
{
 return p.X * p.Y> 100000;
}
Predicate<Point> d = SomePredicate;
 
Predicate<Point> d = delegate(Point p)
{
 return p.X*p.Y> 100000;
};
 
Predicate<Point> d = p => p.X* p.Y> 100000;

Closures

A lambda expression may use variables defined outside its context («outer variables»). here factor

void doSomething()
{
 var factor = 2;
 Func<int, int> multiplier= n => n * factor;
 Console.WriteLine(multiplier(3)); //6
}

Be careful!! Writes the current value of i and not the value of i back when the delegate was created.

var actions = newList<Action>();
for(var i = 0; i < 10; i++)
 actions.Add(() => Console.WriteLine(i));
foreach(var action in actions)
 action(); // 101010101010101...

The solution to this is to declare a new “inner variable" for each iteration to be captured, instead of a single “outer variable" which is captured only once.

for(var i = 0; i < 10; i++)
{
 varj = i; //each iteration gets its own,new variable j
 actions.Add(() => Console.WriteLine(j));
}

LINQ

LINQ = Language INtegrted Query are just like streams in Java. LINQ features can be used in a C# program by importing the System.Linq namespace.

LINQ is executed when results are accessed, not when the query is created. Execution happens when:

  • iterating over results
  • calling immediate execution methods like toList, Count etc.

NEVER modifystate using LINQ!!!

ForEach is NOT LINQ

persons.ForEach(p => Console.WriteLine(p.FirstName));

Method & Query Syntax

Method syntax resembles most other C# method calls, while query syntax resembles SQL. Query must begin with from clause, and end with select or group clause Between first from clause and last select/group clause, it can contain one or more of the following clauses:Where, Orderby, Join, Let, From, Into

// Method syntax
var custQuery2 = customers.Where(c => c.City == "London");
var orderedByLength = names.OrderBy(n => n.Length);
 
// Query syntax
var custQuery =
 FROM c IN customers
 WHERE c.City == "London"
 SELECT c;
// Mix
var results = (FROM c IN Comedians SELECT c).Count();

Simple queries

// **** AGGREGATE ****
var min = array.Min(); // All below can also be called straight on array
var count = array.Count()
var condCount = data.Count(x => x.Condition == true)
var sumAges = persons.Sum(p => p.Age);
var minAge = persons.Min(p => p.Age);
var minAge = persons.Max(p => p.Age);
var avgAge = persons.Average(p => p.Age);
var simplePersons = persons.Select(p => new { Surname = p.Surname, Firstname = p.Firstname}) // creates an enumarable of anonymous class
 
// **** CONVERSIONS ****
data.ToArray(); // Convert to Array
data.ToList(); // Convert to List
data.ToDictionary( x=> x.Name ); // Convert to Dictionary keyed on Name
// **** ELEMENT ****
 
data.First() // Returns the first element
data.First( x=> x.Type == Types.A ) // Returns the first element passing the condition
data.FirstOrDefault() // Returns the first element or default
data.FirstOrDefault( x => x.Type == Types.B ) // Returns the first element passing the condition or default
 
data.Last() // Returns the last element
data.Last( x=> x.Type == Types.A ) // Returns the last element passing the condition
data.LastOrDefault( ) // Returns the last element or default*
data.LastOrDefault( x => x.Type == Types.B ) // Returns the last element passing the condition or default*
 
data.ElementAt(0) // Returns the element at position 0
 
// **** FILTERS ****
 
var even array.Where(x => x%2==0) // Returns all elements passing the condition
 
data.Where(( x, index) => index <= 4 && x.Type == Types.A) // The elements index can be passed into the delegate
 
// **** GENERATION ****
Enumerable.Range(1, 10); // Creates collection of 10 items between 1 and 10
Enumerable.Repeat(1, 10); // Creates a collection of 10 1s.
 
// **** ORDERING ****
 
data.OrderBy(x => x.Name); // Order by Name ASC
data.OrderBy(x => x.Name).ThenBy(x => x.Age); // Order by Name ASC the Age ASC
data.OrderBy(x => x.Name).ThenByDescending(x => x.Age); // Order by Name ASC then Age DESC
data.OrderByDescending (x => x.Name); // Order by Name DESC
data.OrderBy(x => x.Name).Reverse(); // Reverse elements
 
// **** PARTITIONING ****
 
data.Take (3); // Take 3 items
data.Skip (3); // Skip 3 items
 
data.TakeWhile (x=>x.Type ==Types.A); // Take all the items while the condition is met
data.SkipWhile (x=>x.Type ==Types.A); // Skip all the items while the condition is met
 
// **** PROJECTION ****
 
data.Select(x => x.Name); // Select collection of a column
 
data.Select(x => new { Name = x.Name, Age = x.Age }); // Select a collection of columns through an anonymus type
 
// **** QUANTIFIERS ****
if(array.Any(i=> i% 2 == 0))
if(array.All(i=> i% 2 == 0))
 
// **** SET ****
 
 
data.Intersect(dataTwo); // Returns the union / intersection of data; elements in both collections
 
data.Except(dataTwo); // Returns elements in data which are not in dataTwo
 
data.Concat(dataTwo); // Concatonates both collections; appends dataTwo to data

Grouping

var users = new List<User>()
    {
        new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
        new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
        new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
        new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
        new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };
var usersGroupedByCountry = users.GroupBy(user => user.HomeCountry);
foreach(var group in usersGroupedByCountry)
{
 Console.WriteLine("Users from " + group.Key + ":");
 foreach(var user in group)
  Console.WriteLine("* " + user.Name);
}

Distinct

students.Distinct(); // Returns a collection of distinct elements
students.Distinct (new StudentComparer()). // Distinct with providing an equality provider
 
public class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            //First check if both object reference are equal then return true
            if(object.ReferenceEquals(x, y))
            {
                return true;
            }
            //If either one of the object refernce is null, return false
            if (object.ReferenceEquals(x,null) || object.ReferenceEquals(y, null))
            {
                return false;
            }
            //Comparing all the properties one by one
            return x.ID == y.ID && x.Name == y.Name;
        }
        public int GetHashCode(Student obj)
        {
            //If obj is null then return 0
            if (obj == null)
            {
                return 0;
            }
            //Get the ID hash code value
            int IDHashCode = obj.ID.GetHashCode();
            //Get the string HashCode Value
            //Check for null refernece exception
            int NameHashCode = obj.Name == null ? 0 : obj.Name.GetHashCode();
            return IDHashCode ^ NameHashCode;
        }
    }

Join

IList<Student> studentList = new List<Student>() {
    new Student() { StudentID = 1, StudentName = "John", StandardID =1 },
    new Student() { StudentID = 2, StudentName = "Moin", StandardID =1 },
    new Student() { StudentID = 3, StudentName = "Bill", StandardID =2 },
    new Student() { StudentID = 4, StudentName = "Ram" , StandardID =2 },
    new Student() { StudentID = 5, StudentName = "Ron"  }
};
 
IList<Standard> standardList = new List<Standard>() {
    new Standard(){ StandardID = 1, StandardName="Standard 1"},
    new Standard(){ StandardID = 2, StandardName="Standard 2"},
    new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
 
var innerJoin = studentList.Join(// outer sequence
                      standardList,  // inner sequence
                      student => student.StandardID,    // outerKeySelector
                      standard => standard.StandardID,  // innerKeySelector
                      (student, standard) => new  // result selector
                                    {
                                        StudentName = student.StudentName,
                                        StandardName = standard.StandardName
                                    });
/*
John - Standard 1
Moin - Standard 1
Bill - Standard 2
Ram - Standard 2
*/

SelectMany

SelectMany() flattens the resulting sequences into one sequence, and invokes a result selector function on each element therein.

PetOwner[] petOwners =
         { new PetOwner { Name="Higa, Sidney",
              Pets = new List<string>{ "Scruffy", "Sam" } },
           new PetOwner { Name="Ashkenazi, Ronen",
              Pets = new List<string>{ "Walker", "Sugar" } },
           new PetOwner { Name="Price, Vernette",
              Pets = new List<string>{ "Scratches", "Diesel" } } };
 
// Query using SelectMany().
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
 
Console.WriteLine("Using SelectMany():");
 
// Only one foreach loop is required to iterate
// through the results since it is a
// one-dimensional collection.
foreach (string pet in query1)
{
    Console.WriteLine(pet);
}
 
// This code shows how to use Select()
// instead of SelectMany().
IEnumerable<List<String>> query2 =
    petOwners.Select(petOwner => petOwner.Pets);
 
Console.WriteLine("\nUsing Select():");
 
// Notice that two foreach loops are required to
// iterate through the results
// because the query returns a collection of arrays.
foreach (List<String> petList in query2)
{
    foreach (string pet in petList)
    {
        Console.WriteLine(pet);
    }
    Console.WriteLine();
}
/*
Using SelectMany():
Scruffy
Sam
Walker
Sugar
Scratches
Diesel
 
Using Select():
Scruffy
Sam
 
Walker
Sugar
 
Scratches
Diesel
*/