LINQ Operator Drills

A Tour of Must-Know LINQ Operations

George Marklow

--

Photo by Steve Johnson on Unsplash

Introduction

This article helps you if you’re already familiar with C# and LINQ and you need to:

  1. Prepare for interviews, or
  2. Reacquaint yourself with the language after a period away from C# e.g. moving from frontend development in Angular and TypeScript to a backend project.

Each part consists of a “drill” that explores important operations of a few common linq operations.

Names of variables and methods are kept deliberately short to minimize the amount of typing required to complete the code exercises.

What This Article Is Not

This article is about “learning by doing” and doesn’t offer thorough explanations of the operations used. You’re left to explore these operations in more detail on the C# Documentation page in your own time.

Setup

Open a new Replit C# environment (or create a Visual Studio console project):

Ensure you’re using:

  • System.Linq
  • System.Collections.Linq
  • System.Text

To help get you through the drill quicker, I’ve created a method “o” that takes a collection and prints out the contents on a single line with a blank space between items. This reduces the amount of typing and printing to the console.

using System;
using System.Linq;
using System.Collections.Linq;
using System.Text;
class Program
{
public static void Main (string[] args)
{
o(new int[]{1,2});
}
public static void o(IEnumerable<object> collection)
{
StringBuilder sb = new StringBuilder();
sb.Append("[");
var lastIndex = collection.Count() - 1;
for (int i = 0; i <= lastIndex; i++)
{
var value = collection.ElementAt(i);
sb.Append($"{value}");
if (i != lastIndex)
{
sb.Append($", ");
}
}
sb.Append("]");
Console.WriteLine(sb.ToString());
}
}

Hit the Run button at the top and the output is written to the Console on the right-hand side:

[1, 2]

You will need to add an overloaded method to handle the printing of values instead of handling a collection.

public static void o(object value)
{
Console.WriteLine(value);
}

You’re now ready to begin the C# drills!

Filtering

var x = new int[] { 1, 2, 1, 3 };o(x.Distinct());               // [1, 2, 3]
o(x.Where(x => x > 1)); // [2, 3]
o(x.Where(x => x > 3)); // []
o(x.Take(1)); // [1]
o(x.Take(2)); // [1, 2]
o(x.Take(3)); // [1, 2, 1]
o(x.Take(4)); // [1, 2, 1, 3]
o(x.Take(0)); // []
o(x.Take(99)); // [1, 2, 1, 3]
o(x.TakeLast(1)); // [3]
o(x.TakeLast(2)); // [1, 3]
o(x.TakeLast(3)); // [2, 1, 3]
o(x.TakeLast(4)); // [1, 2, 1, 3]
o(x.TakeLast(0)); // []
o(x.TakeLast(99)); // [1, 2, 1, 3]
o(x.TakeWhile(x => x < 3)); // [1, 2, 1]
o(x.TakeWhile(x => x == 1)); // [1]
o(x.TakeWhile(x => x == 2)); // []
o(x.Skip(0)); // [1, 2, 1, 3]
o(x.Skip(1)); // [2, 1, 3]
o(x.Skip(2)); // [1, 3]
o(x.Skip(3)); // [3]
o(x.Skip(4)); // []
o(x.SkipLast(0)); // [1, 2, 1, 3]
o(x.SkipLast(1)); // [1, 2, 1]
o(x.SkipLast(2)); // [1, 2]
o(x.SkipLast(3)); // [1]
o(x.SkipLast(4)); // []
o(x.SkipWhile(x => x < 3)); // [3]
o(x.SkipWhile(x => x == 1)); // [2, 1, 3]
o(x.SkipWhile(x => x == -1)); // [1, 2, 1, 3]

Projecting

First create the following class:

class A
{
public List<int> z { get; set; }
}

Now run the following:

var x = new int[] { 1, 2, 3 };var y = new List<A>
{
new A
{
z = new List<int>{ 1, 2 }
},
new A
{
z = new List<int>{ 3 }
}
};
o(x.Select(x => x * x)); // [1, 4, 9]
o(y.SelectMany(x => x.z)); // [1, 2, 3]

Joining

You will need to set up data using the following classes, which are connected via g:

class B
{
public string f;
public int g;
}
class C
{
public int g;
public string h;
}

Now run the following code:

var x = new List<B>
{
new B { f = "f1", g = 1 },
new B { f = "f2", g = 1 },
new B { f = "f3", g = 2 },
new B { f = "f4", g = 2 }
};
var y = new List<C>
{
new C { g = 1, h = "h1" },
new C { g = 2, h = "h2" }
};
var z1 = x.Join(y,
a => a.g,
b => b.g,
(c, d) => new
{
i = c.f,
j = c.g,
k = d.h
});
o(z1.Select(x => $"({x.i}, {x.j}, {x.k})\n"));// [(f1, 1, h1)
// , (f2, 1, h1)
// , (f3, 2, h2)
// , (f4, 2, h2)
// ]
var z2 = y.GroupJoin(x,
c => c.g,
d => d.g,
(c, list) => new
{
key = c.h,
value = list,
});
foreach (var item in z2)
{
o($"{item.key}:");
o(item.value.Select(x => x.f));
}
// h1:
// [f1, f2]
// h2:
// [f3, f4]

Ordering

You will need to set up data using the following class

class D
{
public string i;
public int j;
public int k;
}

Now run the following code:

var y = new List<D>
{
new D { i = "i1", j = 2, k = 4 },
new D { i = "i2", j = 3, k = 3 },
new D { i = "i3", j = 1, k = 6 },
new D { i = "i4", j = 1, k = 5 }
};
o(y.OrderBy(a => a.j)
.Select(b => $"({b.i}, {b.j}, {b.k})\n"));
// [(i3, 1, 6)
// , (i4, 1, 5)
// , (i1, 2, 4)
// , (i2, 3, 3)
// ]
o(y.OrderByDescending(x => x.j)
.Select(b => $"({b.i}, {b.j}, {b.k})\n"));
// [(i2, 3, 3)
// , (i1, 2, 4)
// , (i3, 1, 6)
// , (i4, 1, 5)
// ]
o(y.OrderBy(a => a.j)
.ThenBy(b => b.k)
.Select(c => $"({c.i}, {c.j}, {c.k})\n"));
// [(i4, 1, 5)
// , (i3, 1, 6)
// , (i1, 2, 4)
// , (i2, 3, 3)
// ]
y.Reverse()
o(y.Select(b => $"({b.i}, {b.j}, {b.k})\n"));
// [(i4, 1, 5)
// , (i3, 1, 6)
// , (i2, 3, 3)
// , (i1, 2, 4)
// ]

Grouping

Here, we will reuse the class D from the previous drill:

class D
{
public string i;
public int j;
public int k;
}

Now run the following code:

var y = new List<D>
{
new D { i = "i1", j = 2, k = 4 },
new D { i = "i2", j = 3, k = 3 },
new D { i = "i3", j = 1, k = 6 },
new D { i = "i4", j = 1, k = 5 }
};
var z = y.GroupBy(a => a.j);foreach (var items in z)
{
o($"{item.Key}:");
o(items.Select(x => $"({x.i}, {x.k})\n"));
}
// 2:
// [(i1, 4)]
// 3:
//[(i2, 3)]
// 1:
// [(i3, 6), (i4, 5)]

Set

var x = new int[] { 1, 2 };
var y = new int[] { 2, 3 };
o(x.Concat(y)); // [1, 2, 2, 3]
o(x.Union(y)); // [1, 2, 3]
o(x.Intersect(y)); // [2]
o(x.Except(y)); // [1]
o(y.Concat(x)); // [2, 3, 1, 2]
o(y.Union(x)); // [2, 3, 1]
o(y.Intersect(x)); // [2]
o(y.Except(x)); // [3]

Element

var x = new int[] { 1, 2, 3 };o(x.First());                     // 1
o(x.Last()); // 3
o(x.ElementAt(0)); // 1
o(x.ElementAt(1)); // 2
o(x.ElementAt(2)); // 3
o(x.ElementAtOrDefault(2)); // 3
o(x.ElementAtOrDefault(3)); // 0

Aggregation

var x = new int[] { 1, 2, 3 };o(x.Count());                               // 3
o(x.Sum()); // 6
o(x.Min()); // 1
o(x.Max()); // 3
o((int)x.Average()); // 2
o(x.Aggregate((x1, x2) => x1 + x2 + 1)); // 8

Quantifiers

var x = new int[] { 1, 2, 3 };o(x.All(x => x > 0));                       // True
o(x.All(x => x > 1)); // False
o(x.Any(x => x > 2)); // True
o(x.Any(x => x > 3)); // False
o(x.Contains(1)); // True
o(x.Contains(0)); // False
o(x.SequenceEqual(new int[] { 1, 2, 3 })); // True
o(x.SequenceEqual(new int[] { 1, 2 })); // False

Generation

o(Enumerable.Empty<int>());       // []
o(Enumerable.Range(1, 3)); // [1, 2, 3]
o(Enumerable.Repeat(0, 3)); // [0, 0, 0]

Thanks for reading! Let me know what you think in the comments section below, and don’t forget to subscribe 👍

--

--

George Marklow

George is a software engineer, author, blogger, and abstract artist who believes in helping others to make us happier and healthier.