More Effective LINQ

Web page by Kevin Harris of Homer IL

Please contact Kevin Harris of Homer IL concerning this web site

LINQ Examples I

Language-Integrated Query (LINQ) is a query language build into the .NET languages. LINQ was released with .NET 3.5 in November 2007 (C# 3.0). LINQ was built on an evolution of powerful language features which include anonymous types, generics, lambda expressions (anonymous functions), type inference, and extension methods. See the LINQ Fundamentals article for general information about LINQ and related technologies. This article focuses on writing LINQ and making use of its many extension methods.

LINQ has a Query Expression Syntax (which resembles SQL) and a popular Method Syntax which allows the chaining of methods to form a processing pipeline. The compiler will convert the Query syntax to Method syntax. The LINQ methods are extension methods on IEnumerable <T>. LINQPad is a handy software utility which allows you to query various databases in LINQ (or SQL). It also allows you to test C#, F#, and VB code. It is super lightweight with a free standard version and more capable commercial versions. Many programmers have used LINQPad to learn and experiment with LINQ.

.LINQPad by Joseph Albahari


LINQPad by Joseph Albahari

LINQ allows the refactoring of code into a more Declarative style (what we want to do), instead of using an Imperative style (how to do it). Declarative style removes the implementation details and makes the code more succinct and readable. The LINQ extension methods are listed on the Enumerable Methods MSDN page. You can also view them in visual studio by using Go to Definition (F12) or Peek Definition (Alt-F12) for the Enumerable class. Some of these methods include:

  • Aggregate - Applies an accumulator function over a sequence.
  • All - Determines whether all elements of a sequence satisfy a condition.
  • Any - Determines whether a sequence contains any elements.
  • AsEnumerable - Returns the input typed as System.Collections.Generic.IEnumerable`1.
  • Average - Computes the average of a sequence.
  • Cast - Casts the elements of an System.Collections.IEnumerable to the specified type.
  • Concat - Concatenates two sequences.
  • Contains - Determines whether a sequence contains a specified element by using the default equality comparer.
  • Count - Returns the number of elements in a sequence.
  • DefaultIfEmpty - Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty.
  • Distinct - Returns distinct elements from a sequence by using the default equality comparer to compare values.
  • ElementAt - Returns the element at a specified index in a sequence.
  • ElementAtOrDefault -
  • Empty - Returns the element at a specified index in a sequence or a default value if the index is out of range.
  • Except - Produces the set difference of two sequences by using the default equality comparer to compare values.
  • First - Returns the first element of a sequence.
  • FirstOrDefault - Returns the first element of a sequence, or a default value if the sequence contains no elements.
  • GroupBy - Groups the elements of a sequence according to a specified key selector function.
  • GroupJoin - Correlates the elements of two sequences based on key equality and groups the results. A specified System.Collections.Generic.IEqualityComparer`1 is used to compare keys.
  • Intersect - Produces the set intersection of two sequences by using the default equality comparer to compare values.
  • Join - Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys.
  • Last - Returns the last element of a sequence.
  • LastOrDefault - Returns the last element of a sequence, or a default value if the sequence contains no elements.
  • LongCount - Returns an System.Int64 that represents the total number of elements in a sequence.
  • Max - Returns the maximum value in a sequence.
  • Min - Returns the minimum value in a sequence.
  • OfType - Filters the elements of an System.Collections.IEnumerable based on a specified type.
  • OrderBy - Sorts the elements of a sequence in ascending order according to a key.
  • OrderByDescending - Sorts the elements of a sequence in descending order according to a key.
  • Range - Generates a sequence of integral numbers within a specified range.
  • Repeat - Generates a sequence that contains one repeated value.
  • Reverse - Inverts the order of the elements in a sequence.
  • Select - Projects each element of a sequence into a new form by incorporating the element's index.
  • SelectMany - Projects each element of a sequence to an System.Collections.Generic.IEnumerable`1, and flattens the resulting sequences into one sequence. The index of each source element is used in the projected form of that element.
  • SequenceEqual - Determines whether two sequences are equal by comparing the elements by using the default equality comparer for their type.
  • Single - Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
  • SingleOrDefault - Returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence.
  • Skip - Bypasses a specified number of elements in a sequence and then returns the remaining elements.
  • SkipWhile - Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
  • Sum - Computes the sum of a sequence.
  • Take - Returns a specified number of contiguous elements from the start of a sequence.
  • TakeWhile - Returns elements from a sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function.
  • ThenBy - Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
  • ThenByDescending - Performs a subsequent ordering of the elements in a sequence in descending order by using a specified comparer.
  • ToDictionary - Creates a System.Collections.Generic.Dictionary`2 from an System.Collections.Generic.IEnumerable`1 according to specified key selector and element selector functions.
  • ToList - Creates a System.Collections.Generic.List`1 from an System.Collections.Generic.IEnumerable`1.
  • ToLookup - Creates a System.Linq.Lookup`2 from an System.Collections.Generic.IEnumerable`1 according to specified key selector and element selector functions.
  • Union - Produces the set union of two sequences by using the default equality comparer.
  • Where - Filters a sequence of values based on a predicate.
  • Zip - Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.


Imperative Code Converted to Declarative Code

Below are examples of imperative code which have been converted to declaritive code with LINQ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var employees = new[] {
	new { LastName = "Jones", FirstName = "John" },
	new { LastName = "Smith", FirstName = "" },
	new { LastName = "Harris", FirstName = "Kevin" },
	new { LastName = "Hart", FirstName = "" }
};

foreach (var employee in employees.Where(e=> !String.IsNullOrEmpty(e.FirstName)))
{
	Console.WriteLine(employee.LastName);	
}


Nested Looped Flattened with Where Extension Method

The following code shows how to replace a ForEach loop, used for finding an item in a collection, with a LINQ statement. Instead of the FirstOrDefault method, the First method could be used which would generate an "InvalidOperationException - Sequence contains no matching element" exception if the item is not found in the collection.

.Replace Foreach Search Loop with LINQ


Replace Foreach Search Loop with LINQ


Any and All Methods

This code uses the Any and All methods to find any unpaid accounts and to determine if all accounts were paid.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
void Main()
{
	if (CheckForUnpaid())	
	"Found Unpaid order(s)".Dump();
	
	if (AllOrdersPaid())	
	"All orders were paid".Dump();	
}

class Order
{
	public int Id {get; set;}
	public decimal Amount {get; set;}
	public string Name {get; set;}
	public string Status {get; set;}
}

List<Order> orders = new List<Order>()
{
	new Order  { Id = 1, Amount = 19.95m, Name = "Smith", Status = "Paid" },
	new Order  { Id = 2, Amount = 29.95m, Name = "Jones", Status = "Not Paid" },
	new Order  { Id = 3, Amount = 39.95m, Name = "Black", Status = "Paid" },
	new Order  { Id = 4, Amount = 49.95m, Name = "White", Status = "Paid" }
};

bool CheckForUnpaid ()
{
	bool anyUnpaid = orders.Any(o => o.Status == "Not Paid");
//	bool anyUnpaid = false;
//	foreach (var order in orders)
//	{
//		if (order.Status == "Not Paid")
//		{
//			anyUnpaid = true;
//			break;
//		}
//	}
	return anyUnpaid;
}

bool AllOrdersPaid()
{
	bool allPaid = orders.All(o => o.Status == "Paid");
//	bool allPaid = true;
//	foreach (var order in orders)
//	{
//		if (order.Status != "Paid")
//		{
//			allPaid = false;
//			break;
//		}
//	}
	return allPaid;


}


Any and All Methods


Select Method Transformation

The following example uses the Select method to transform a list of file names into a list of file sizes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void Main()
{
	var paths = new[] { "c:\\windows\\Microsoft.NET\\Framework\\V4.0.30319\\aspnet_regiis.exe",
						"c:\\windows\\Microsoft.NET\\Framework\\V4.0.30319\\aspnet_compiler.exe",
						"c:\\windows\\Microsoft.NET\\Framework\\V4.0.30319\\aspnet_regbrowsers.exe" };
	//GetListOfFileSizes(paths).Dump();
	paths.Select(p => new FileInfo(p).Length).ToList().Dump();
	paths.Select(p => new FileInfo(p)).ToDictionary(p => p.Name, p=> p.Length).Dump();
}

//List<long> GetListOfFileSizes(IEnumerable<string> paths)
//{
//	var fileSizes = new List<long>();
//	foreach (var path in paths)
//	{
//	var length = new FileInfo(path).Length;
//	fileSizes.Add(length);
//	}
//	return fileSizes;
//}


Select Method Transforming List of File Names to List of File Sizes


Count and Sum Methods

This code uses the Count and Sum methods to the lines of code from 8 to 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void Main()
{
	String.Format("Count of paid orders is: {0}", CountPaidOrders()).Dump();
	String.Format("Amount of paid orders is: {0:C}", SumPaidOrders()).Dump();	
}

class Order
{
	public int Id {get; set;}
	public decimal Amount {get; set;}
	public string Name {get; set;}
	public string Status {get; set;}
}

List<Order> orders = new List<Order>()
{
	new Order  { Id = 1, Amount = 19.95m, Name = "Smith", Status = "Paid" },
	new Order  { Id = 2, Amount = 29.95m, Name = "Jones", Status = "Not Paid" },
	new Order  { Id = 3, Amount = 39.95m, Name = "Black", Status = "Paid" },
	new Order  { Id = 4, Amount = 49.95m, Name = "White", Status = "Paid" }
};

private int CountPaidOrders()
{	
//	int paidCount = 0;
//	foreach (var order in orders)
//	{
//		if (order.Status == "Paid")
//		{
//			paidCount++;
//		}
//	}
//	return paidCount;
	return orders.Count(o => o.Status == "Paid");
}

private decimal SumPaidOrders()
{
//	decimal paidSum = 0;
//	foreach (var order in orders)
//	{
//		if (order.Status == "Paid")
//		{
//			paidSum += order.Amount;
//		}
//	}
//	return paidSum;
	return orders.Sum(o => o.Amount);
}


Sum and Count Methods Reduce the Number of Lines Required


ToDictionary and ToList Methods

The following code creates a dictionary containing a list of customers using the ToDictionary and ToList methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void Main()
{
	//OrdersByCustomer().Dump();	
	orders.GroupBy(o => o.Name)
	.ToDictionary(g => g.Key, g=> g.ToList())
	.Dump();
}

//private Dictionary<string, List<Order>> OrdersByCustomer()
//{	
//	var dict = new Dictionary<string, List<Order>>();
//	foreach (var order in orders)
//	{
//		if (!dict.ContainsKey(order.Name))
//			dict[order.Name] = new List<Order>();
//		dict[order.Name].Add(order);
//	}
//	return dict;
//}

class Order
{
	public int Id {get; set;}
	public decimal Amount {get; set;}
	public string Name {get; set;}
	public string Status {get; set;}
}

List<Order> orders = new List<Order>()
{
	new Order  { Id = 1, Amount = 19.95m, Name = "Smith", Status = "Paid" },
	new Order  { Id = 2, Amount = 29.95m, Name = "Jones", Status = "Not Paid" },
	new Order  { Id = 3, Amount = 39.95m, Name = "Black", Status = "Paid" },
	new Order  { Id = 4, Amount = 49.95m, Name = "White", Status = "Paid" },
	new Order  { Id = 1, Amount = 23.95m, Name = "Smith", Status = "Paid" },
	new Order  { Id = 2, Amount = 45.95m, Name = "Jones", Status = "Not Paid" },
	new Order  { Id = 3, Amount = 64.95m, Name = "Black", Status = "Paid" },
	new Order  { Id = 4, Amount = 35.95m, Name = "White", Status = "Paid" },
	new Order  { Id = 2, Amount = 76.95m, Name = "Jones", Status = "Paid" },
	new Order  { Id = 3, Amount = 54.95m, Name = "Black", Status = "NotPaid" },
	new Order  { Id = 4, Amount = 49.95m, Name = "White", Status = "Paid" }

};
.Dictionary Containing a List of Customers


Dictionary Containing a List of Customers


Custom Extension Methods



Sum Timespan Lengths

The following LINQ calculates the total length of songs on an album by converting them to timespans and using the Aggregate method.

1
2
3
4
5
6
7
8
9
void Main()
{ 
	"2:54,3:48,4:45,5:55,3:57,8:31,7:32,3:59,4:44,8:54"
	.Split(',')
	.Select(t => "0:" + t)
	.Select(t => TimeSpan.Parse(t))
	.Aggregate((t1,t2) => t1 + t2)
	.Dump();
}


Calculates the Length of Songs on an Album

The code below is equivalent to the code above except a custom extension method was created. This allows the implementation details to be moved out of the LINQ pipeline and put into the extension method. That way the LINQ pipeline just contains the "intent" of the process without the noise of the implementation details.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Main()
{ 
	"2:54,3:48,4:45,5:55,3:57,8:31,7:32,3:59,4:44,8:54"
	.Split(',')
	.Select(t => "0:" + t)
	.Select(t => TimeSpan.Parse(t))
	.Sum()
	.Dump();
}


static class MyLinqExtenstions
{
	public static TimeSpan Sum(this IEnumerable<TimeSpan> times)
	{
		var total = TimeSpan.Zero;
		foreach (var time in times)
		{
			total += time;
		}
		return total;
	}
}


Custom Extension Method Used in LINQ Pipeline


Range Method

The next LINQ expression converts a list of range numbers into the individual numbers. The output is: 2, 5, 7, 8, 9, 10, 11, 17, 18.

1
2
3
4
5
6
7
8
9
void Main()
{ 
	"2,5,7-10,11,17-18"
	.Split(',')
	.Select(x => x.Split('-'))
	.Select(p => new {First = int.Parse(p[0]), Last= int.Parse(p.Last()) })
	.SelectMany(r => Enumerable.Range(r.First, r.Last - r.First + 1))
	.Dump();
}


Convert a List of Range Numbers Into Individual Numbers


Concatenate Strings

Below is a custom extension method used to concatenate strings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void Main()
{ 
	"2,5,7-10,11,17-18"
	.Split(',')
	.Select(x => x.Split('-'))
	.Select(p => new {First = int.Parse(p[0]), Last= int.Parse(p.Last()) })
	.SelectMany(r => Enumerable.Range(r.First, r.Last - r.First + 1))
	.Select(n => n.ToString())
	.StringConcat(",")
	.Dump();
}

static class MyLinqExtenstions
{
   public static string StringConcat(this IEnumerable<string> strings, string separator)
   {
   		return string.Join(separator, strings);
   }
}


Custom Extension Method to Concatenate Strings


Categorize and Count Members in List


The following categorizes and counts the members in a string list.

1
2
3
4
5
6
7
8
9
void Main()
{ 
	"Dog, Cat, Rabbit, Dog, Dog, Lizard, Cat, Pig, Rabbit, Dog, Cat"
	.Split(',')
	.Select(a => a.Trim())
	.GroupBy(x => (x != "Dog" && x != "Cat") ? "Other" : x)
	.Select(g => new { Pet = g.Key, Count = g.Count() })
	.Dump();
}


Categorize and Count Members in List

Same as above code but using a custom generic extension method (CountBy) to hold the implementation details of grouping and counting the list members.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void Main()
{ 
	"Dog, Cat, Rabbit, Dog, Dog, Lizard, Cat, Pig, Rabbit, Dog, Cat"
	.Split(',')
	.Select(a => a.Trim())
	.CountBy(x => (x != "Dog" && x != "Cat") ? "Other" : x)
	.Dump();
}

static class MyLinqExtensions
{
	public static IEnumerable<KeyValuePair<TKey, int>> CountBy<TSource, TKey>(this IEnumerable<TSource> source,
														Func<TSource, TKey> selector)
	{
		var counts = new Dictionary<TKey, int>();
		foreach (var item in source)
		{
			var key = selector(item);
			if (!counts.ContainsKey(key))
			{
				counts[key] = 1;
			}
			else
			{
				counts[key]++;
			}
		}
		return counts;
	}
}														


Categorize and Count Members in List - Using Generic Extension Method


MoreLINQ


MoreLINQ on NuGet

MoreLINQ contains several custom LINQ extension methods which are very useful. It is on open-source project hosted on NuGet at
MoreLINQ.


The following code uses the zip method to calculate lap times.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void Main()
{ 
	var lapTimes = "00:50, 02:10, 03:40, 05:45, 08:20, 11:10";
	
	("00:00," + lapTimes)
	.Split(',')
	.Select(t => t.Trim())
	.Zip(lapTimes.Split(',').Select(t => t.Trim()),
		(s, f) => new {
		Start = TimeSpan.Parse("00:" + s),
		Finish = TimeSpan.Parse("00:" + f)
	})
	.Select (q => q.Finish-q.Start)	
	.Dump();
}


Calculate Lap TImes using Zip Method

The following code performs the same process as above (calculating lap times), except it uses MoreLINQ's Prepend and Pairwise Methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void Main()
{ 
	var lapTimes = "00:50, 02:10, 03:40, 05:45, 08:20, 11:10";
	
	//("00:00," + lapTimes)
	lapTimes
	.Split(',')
	.Select(t => t.Trim())
	.Select(x => TimeSpan.Parse("00:" + x))
	.Prepend(TimeSpan.Zero)
	.Pairwise((a, b) => b - a)
//	.Zip(lapTimes.Split(',').Select(t => t.Trim()),
//		(s, f) => new {
//		Start = TimeSpan.Parse("00:" + s),
//		Finish = TimeSpan.Parse("00:" + f)
//	})
//	.Select (q => q.Finish-q.Start)	
	.Dump();
}

static partial class MoreEnumerable
{
	public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource value)
	{
		if (source == null) throw new ArgumentNullException("source");
    		return Enumerable.Concat(Enumerable.Repeat(value, 1), source);
	}		
	
	public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)
    {
    	if (source == null) throw new ArgumentNullException("source");
    	if (resultSelector == null) throw new ArgumentNullException("resultSelector");
    	return PairwiseImpl(source, resultSelector);
    }	
	
       private static IEnumerable<TResult> PairwiseImpl<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)
        {
            Debug.Assert(source != null);
            Debug.Assert(resultSelector != null);

            using (var e = source.GetEnumerator())
            {
                if (!e.MoveNext())
                    yield break;

                var previous = e.Current;
                while (e.MoveNext())
                {
                    yield return resultSelector(previous, e.Current);
                    previous = e.Current;
                }
            }
        }	
	
}	


Calculate Lap Times using MoreLINQ's Prepend and Pairwise Methods

MoreLINQ's Batch Method

The following code uses MoreLINQ's Batch method to assign groups of lines from a file to variables.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
void Main()
{ 
File.ReadAllLines(@"c:\Docs\questions.txt")
.Batch(7, b => b.ToArray())
.Select(b => new {
Key = b[0],
Question = b[1],
CorrectAnswer = b[2],
WrongAnswer1 = b[3],
WrongAnswer2 = b[4],
WrongAnswer3 = b[5]
})
.Dump();
}

static partial class MoreEnumerable
{
        public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(this IEnumerable<TSource> source, int size)
        {
            return Batch(source, size, x => x);
        }	
		
        public static IEnumerable<TResult> Batch<TSource, TResult>(this IEnumerable<TSource> source, int size,
            Func<IEnumerable<TSource>, TResult> resultSelector)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (size <= 0) throw new ArgumentOutOfRangeException("size");
            if (resultSelector == null) throw new ArgumentNullException("resultSelector");
            return BatchImpl(source, size, resultSelector);
        }		
		
        private static IEnumerable<TResult> BatchImpl<TSource, TResult>(this IEnumerable<TSource> source, int size,
            Func<IEnumerable<TSource>, TResult> resultSelector)
        {
            Debug.Assert(source != null);
            Debug.Assert(size > 0);
            Debug.Assert(resultSelector != null);

            TSource[] bucket = null;
            var count = 0;

            foreach (var item in source)
            {
                if (bucket == null)
                {
                    bucket = new TSource[size];
                }

                bucket[count++] = item;

                // The bucket is fully buffered before it's yielded
                if (count != size)
                {
                    continue;
                }

                // Select is necessary so bucket contents are streamed too
                yield return resultSelector(bucket.Select(x => x));
               
                bucket = null;
                count = 0;
            }	
		}
}	


MoreLINQ's Batch Method

.MoreLINQ's Batch Method


MoreLINQ's Batch Method


Helper Methods


The following code take a list of name and birthdays and returns their name/age in descending order.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Main()
{ 
	"Dick Van Dyke, 12/13/1925; Jerry Van Dyke, 7/27/1931; Gene Hackman, 1/30/1930"
	.Split(';')
	.Select(n => n.Split(','))
	.Select(n => new { Name=n[0].Trim(), DOB=ParseDob(n[1])})
	.OrderBy(n => n.DOB)
	.Select(n => new {Name=n.Name, Age = GetAge(n.DOB)})
	.Dump();
}

Func<String, DateTime> ParseDob = dob =>  DateTime.ParseExact(dob.Trim(), "M/d/yyyy", System.Globalization.CultureInfo.InvariantCulture);
//static DateTime ParseDob(String dob)
//{
//	return DateTime.ParseExact(dob.Trim(), "M/d/yyyy", System.Globalization.CultureInfo.InvariantCulture);
//}
//
static int GetAge(DateTime dob)
{
	DateTime today = DateTime.Today;
	int age = today.Year - dob.Year;
	if (dob > today.AddYears(-age)) age --;
	return age;
}
.Calculate Ages


Calculate Ages



Determine Valid Chess Moves

The following code determines the valid chess moves for a bishop on C6.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void Main()
{ 
	GetBoardPositions()
	.Where(p => BishopCanMoveTo(p, "c6"))
	.Dump();
}

static IEnumerable<String> GetBoardPositions()
{
//	return Enumerable.Range('a', 8).SelectMany(
//		x => Enumerable.Range('1', 8), (f, r) =>
//		string.Format("{0}{1}",(char)f, (char)r));
		
	return
	from file in Enumerable.Range('a', 8)
	from rank in Enumerable.Range('1', 8)
	select string.Format("{0}{1}",(char)file, (char)rank);
}

static bool BishopCanMoveTo(string startPos, string targetPos)
{
	var dx = Math.Abs(startPos[0] - targetPos[0]);
	var dy = Math.Abs(startPos[1] - targetPos[1]);
	return dx == dy && dx != 0;
}
.Chess Board


Determine Chess Moves for Bishop on C6

Tags: 
Error | ASP.NET Developer

Error

Error message

  • Warning: Cannot modify header information - headers already sent by (output started at /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/common.inc:2748) in drupal_send_headers() (line 1232 of /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/bootstrap.inc).
  • PDOException: SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user '1218369_b2cf'@'185.176.40.58' for table 'watchdog': INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9); Array ( [:db_insert_placeholder_0] => 0 [:db_insert_placeholder_1] => cron [:db_insert_placeholder_2] => Attempting to re-run cron while it is already running. [:db_insert_placeholder_3] => a:0:{} [:db_insert_placeholder_4] => 4 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => http://www.kcshadow.net/aspnet/?q=node/115 [:db_insert_placeholder_7] => [:db_insert_placeholder_8] => 54.80.87.62 [:db_insert_placeholder_9] => 1534851793 ) in dblog_watchdog() (line 160 of /srv/disk9/1218369/www/kcshadow.net/aspnet/modules/dblog/dblog.module).
The website encountered an unexpected error. Please try again later.