4
\$\begingroup\$

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?
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

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();
\$\endgroup\$
5
  • \$\begingroup\$ Ok, I'll bite. Why is it not this IEnumerable<T[ , ]> values \$\endgroup\$
    – radarbob
    Commented 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
    Commented 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. Does i autoincrement? I'm sorry if I'm asking a really stupid question here. \$\endgroup\$
    – yu_ominae
    Commented Oct 31, 2016 at 12:47
  • \$\begingroup\$ @yu_ominae the Select has an overload Enumerable.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
    Commented 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
    Commented Oct 31, 2016 at 12:56
0
\$\begingroup\$

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;
    }
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.