Alexandar
Alexandar

Reputation: 936

Issue with LINQ

I created LINQ implementation of mod10 algorithm.

The source code:

string number = "7992739871";
int mod10sum = number.Reverse()
    .Select((c, i) => (c - '0') << ((i + 1) & 1)) // Double every other digit and sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5) 
    .Sum(c => c - '0') % 10;                      // together with the undoubled digits from the original number

string checkDigit = (mod10sum == 0 ? 0 : 10 - mod10sum).ToString("0");
Console.WriteLine(checkDigit);

As per the example, 7992739871 number should have check digit as 3; however, what I am getting is 15.

What I am doing wrong? I am sure the mistake is very small but can't find it.

Upvotes: 0

Views: 196

Answers (2)

Tom
Tom

Reputation: 26829

The problem is with the Select method. In order to sum up all the digits (as described in the algorithm) you would need to return 1 and 0 instead of 10, 1 and 4 instead of 14 (as in your example).

The easiest (but it does not have to be the most optimal) way to do that is to conert number from Select to string (14 -> "14") and then split the string characters using SelectMany.

So your code should look as follows:

int mod10sum = number.Reverse()
    .SelectMany((c, i) => ((c - '0') << ((i + 1) & 1)).ToString())
    .Sum(c => c - '0') % 10;

checkDigit = (mod10sum == 0 ? 0 : 10 - mod10sum).ToString("0");
Console.WriteLine(checkDigit);

A bit of theory

LINQ SelectMany returns IEnumerable<>. When you return string (which is IEnumerable) then that's why SelectMany "splits" returned string into characters.

Microsoft has very nice page (101 LINQ Samples) with different LINQ samples which should help you out.

EDIT

I would also recommend working on that conversion from int to string. I was working on similar project literally yesterday and in my case that conversion is a bit problematic from performance point of view as we call that method millions of times. If you have to calculate lots of mod10's then it might be not the best solution.

Upvotes: 7

Rawling
Rawling

Reputation: 50114

I would change the Sum.

At this point, you don't have a sequence of characters, but the single-or-doubled-as-appropriate value for each original digit.

Thus, you don't need to be subtracting 0, you need to be calculating the digit sum of each of these integers, and (since you know they'll be small) you can do this as simply as

.Sum(i => (i % 10) + (i / 10))

giving

string number = "7992739871";
int mod10sum = number.Reverse()
    .Select((c, i) => (c - '0') << ((i + 1) & 1)) 
    .Sum(i => (i % 10) + (i / 10)) % 10;

This should be more effecient than calling ToString() and iterating over the result.

Upvotes: 2

Related Questions