Recently, I have been trying my hands on LINQ. So I've implemented the diagonal difference using Linq in Hackerrank. I know a similar question has been asked in python Diagonal Difference
Here is an excerpt of the problem Hackerrank -Diagonal Difference
Given a square matrix of size N X N , calculate the absolute difference between the sums of its diagonals.
Input Format
The first line contains a single integer, N . The next lines denote the matrix's rows, with each line containing space-separated integers describing the columns.
Output Format
Print the absolute difference between the two sums of the matrix's diagonals as a single integer.
Sample Input
3
11 2 4
4 5 6
10 8 -12
Sample Output
15
Explanation
The primary diagonal is:
11
5
-12
Sum across the primary diagonal: 11 + 5 - 12 = 4
The secondary diagonal is:
4
5
10
Sum across the secondary diagonal: 4 + 5 + 10 = 19
Difference: |4 - 19| = 15
My Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution {
static void Main(String[] args) {
int n = Convert.ToInt32(Console.ReadLine());
int[][] a = new int[n][];
for(int a_i = 0; a_i < n; a_i++){
string[] a_temp = Console.ReadLine().Split(' ');
a[a_i] = Array.ConvertAll(a_temp,Int32.Parse);
}
int value = -1;
IEnumerable<int> leftDiagonal= a.Select((x) => x.ElementAt(value +1));
int total1 = 0, total2=0;
foreach (var b in leftDiagonal)
{
total1 += b;
value++;
}
int value2 = a.Length;
IEnumerable<int> ans2 = a.Select((x) => x.ElementAt(value2 - 1));
foreach (var b1 in ans2)
{
total2 += b1;
value2--;
}
Console.WriteLine(Math.Abs(total1 - total2));
}
}
Final Statement
- I'm looking for improvements in terms of syntax, style, alternatives, and performance . I'm aware of the naming convention but some of these were the pre-defined structure in Hackerrank
- Using Linq this way is it an overkill for the small data?
2 Answers 2
This doesn't look very linq-ish and clean yet. You mixed the algorithm with console output. Everything inside a single method. That needs separation.
Start by creating two extension methods giving you the numbers for the calculations:
public static IEnumerable<T> PrimaryDiagonal<T>(this IEnumerable<T[]> values)
{
return values.Select((x, i) => x[i]);
}
public static IEnumerable<T> SecondaryDiagonal<T>(this IEnumerable<T[]> values)
{
return values.Reverse().Select((x, i) => x[i]);
}
Now you can calculate whatever you want:
var nums = new[]
{
new [] { 11, 2, 4 },
new [] { 4, 5, 6 },
new [] { 10, 8, - 12 }
};
var primarySum = nums.PrimaryDiagonal().Sum();
var secondarySum = nums.SecondaryDiagonal().Sum();
-
\$\begingroup\$ Ok, I'll bite. Why is it not
this IEnumerable<T[ , ]> values
\$\endgroup\$radarbob– radarbob2016年10月30日 23:59:21 +00:00Commented Oct 30, 2016 at 23:59 -
\$\begingroup\$ @radarbob the first reason is that LINQ cannot work with multidimensional arrays like
T[,]
, the second reason is that this would mean we have a collection of multidimensional arrays which would mean it's three dimensional here ;-) \$\endgroup\$t3chb0t– t3chb0t2016年10月31日 05:03:10 +00:00Commented Oct 31, 2016 at 5:03 -
\$\begingroup\$ I have a really stupid question, but how does
values.Select((x, i) => x[i])
select the values at index 0, 1 and then 2 in the array collection you pass it. Doesi
autoincrement? I'm sorry if I'm asking a really stupid question here. \$\endgroup\$yu_ominae– yu_ominae2016年10月31日 12:47:43 +00:00Commented Oct 31, 2016 at 12:47 -
\$\begingroup\$ @yu_ominae the
Select
has an overloadEnumerable.Select<TSource, TResult> Method (IEnumerable<TSource>, Func<TSource, Int32, TResult>)
which not only gives you the current item but also its index thus(x, i)
. And becuase the matrix is required to be NxN we can use this index to select the i-th item from the current array. \$\endgroup\$t3chb0t– t3chb0t2016年10月31日 12:52:27 +00:00Commented Oct 31, 2016 at 12:52 -
\$\begingroup\$ Holy cow, I've been using
Select
for years and without ever noticing the overload... That will come in handy some day, thank you! \$\endgroup\$yu_ominae– yu_ominae2016年10月31日 12:56:53 +00:00Commented Oct 31, 2016 at 12:56
I would recommend to refactor your long Main() method.
I suggest initialize int value = 0
instead of int value = -1
. Then you can use directly x.ElementAt(value)
. It makes better sense. The code tells me 'grab element at position equal to value'.
You saved one row when you declared int total1 = 0, total2=0;
. For better readability I would split those variables into two lines. Even you can use only one total variable and reuse it if the meaning is the same (hold total value).
Consider more descriptive naming of all used variables (a, ans2, a_i). Use capitalization convensions instead of underscore (https://msdn.microsoft.com/en-us/library/ms229043(v=vs.100).aspx)
I think LINQ is unnecessarily heavy tool for this purpose, but why don't experiment. If you are looking for alternative code, here is the simple 'for-loop' solution.
static void Main(string[] args)
{
// Your part of code that creates a[n][n] array is ommited here
int total1 = getDiagonalSumFromTop(a);
int total2 = getDiagonalSumFromBottom(a);
Console.WriteLine(Math.Abs(total1 - total2));
}
private static int getDiagonalSumFromTop(int[][] array2D)
{
int sum = 0;
for (int i = 0; i < array2D.Length; i++)
{
sum += array2D[i][i];
}
return sum;
}
private static int getDiagonalSumFromBottom(int[][] array2D)
{
int sum = 0;
for (int i = array2D.Length - 1; i >= 0; i--)
{
sum += array2D[array2D.Length - i - 1][i];
}
return sum;
}