Falco Alexander
Falco Alexander

Reputation: 3332

LINQ expression behaves differently on literals?

in a different SO question it was asked to get a signed integer reversed digit-wise: 123 becomes 321 and -123 becomes -321

this is an obvious, quite readable implementation:

int reverse (int i) {
    int sign = i/Math.Abs(i);
    var abs = string.Concat(Math.Abs(i).ToString().Reverse());
    return int.Parse(abs)*sign;
}

this more compact LINQ expression was also suggested: .ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');

but I do not understand the results of the latter:

int j = -123;

int a =  j.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');
int b = -(123.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0'));
int c = -123.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');
int d = -(123).ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');
Console.WriteLine($"{a} , {b} , {c} , {d}");

results in

3207 , -321 , -321 , -321

why does it only work with a literal int? whats is my fallacy, what do I assume wrongly?

Upvotes: 0

Views: 108

Answers (2)

Michael Puckett II
Michael Puckett II

Reputation: 6749

With a you're starting with -123 but with the others you're starting with 123 and making the result -.

You need to enclose 123 like so (-123)

Check it out.

using System;
using System.Linq;

public class Program
{

    public static void Main()
    {
        int j = -123;

        int a = j.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');
        int b = -(123.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0'));
        int c = -123.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');
        int d = -(123).ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');

        //Make -123 like line below.
        int e = (-123).ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');

        Console.WriteLine($"{a} , {b} , {c} , {d}, {e}");
        Console.ReadKey();
    }
}
//OUTPUTS
//3207 , -321 , -321 , -321, 3207

Edit

I answered above why you weren't getting the proper results with the method but I did not answer how it could be achieved. For now I concur with you; "there is not nice LINQ expression for this". However; I was able to turn it into a decently readable one line of code that became a beautiful looking extension method.

I did take a look at the comments below the question and I borrowed from Falco Alexander's comment to use Math.Sign a feature I didn't know existed honestly. I was going to do it manually with a if x < 0 return result * -1 sorta deal.

I also noticed an issue with the current Aggregate function being used and that's that it doesn't support decimals. Currently it's only int but I took it up a notch and tried with double as well. To add the decimal point I aggregated a string; but then I assumed aggregating a string is a bad thing since we only want to perform string computations as little as possible and just made a new string (which breaks the inline LINQ look but performs much better I'm assuming) Anyway; I ended up with what you see below, almost identical to what you have, but working negatives and decimals.

using System;
using System.Linq;

public class Program
{
    public static void Main()
    {
        int j = -123;        
        int k = 123;        
        double l = -123.456;
        double m = 123.456;

        Console.WriteLine(j.Reverse());
        Console.WriteLine(k.Reverse());
        Console.WriteLine(l.Reverse());
        Console.WriteLine(m.Reverse());
        Console.ReadKey();
    }
}

public static class Extensions
{
    public static int Reverse(this int value)
    {
        return Math.Sign(value) * int.Parse(new string(Math.Abs(value).ToString().Reverse().ToArray()));
    }

    public static double Reverse(this double value)
    {
        return Math.Sign(value) * double.Parse(new string(Math.Abs(value).ToString().Reverse().ToArray()));
    }
}
//OUTPUTS
//-321
//321
//-654.321
//654.321

Upvotes: 2

Fabjan
Fabjan

Reputation: 13676

The reason is that in all cases except for a the int converted to string is "123" and not "-123". The minus sign is applied to the result of execution and not used in a .ToString call

In this expression:

-123.ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0')

The .ToString() will be called on "123" and then the rest of LINQ query is executed with Aggregate returning int and only then the minus sign will be applied to resulting int number. An illustration of it would be:

-1 * 123.ToString().Reverse().Aggregate(...)

Even this expression:

int d = -(123).ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0');

Will be transformed to something like:

int d = -1 * (123).someExpr

First compiler removes redundant parentheses, then executes expression and at last it changes the sign of result to minus

P.S.

As @PetSerAI correctly pointed out you could modify LINQ expression to make it work as follows:

int d = Math.Sign(j) * Math.Abs(j).ToString().Reverse().Aggregate(0, (y, x) => 10 * y + x - '0')

Upvotes: 3

Related Questions