Reverse an integer regardless of its sign

Hello.

Trying to reverse an integer regardless of its sign I wrote the following algorithm:

int = -123456789 #=> Expected result -987654321

def minus_one(int) do
    int * -1
end

def sign(int) when int > 0 do
    int
    |> Integer.to_string()
    |> String.reverse()
    |> String.to_integer()
end

def sign(int) do
    int
    |> Integer.to_string()
    |> String.replace("-", "")
    |> String.reverse()
    |> String.to_integer()
    |> minus_one()
end

def reverse_integer(int) do
    sign(int)
end

Is there a native way to get the sign of a number? like :math.sign()

Any suggestions to improve the algorithm is welcome.

Thanks.

def sign(int) when int >= 0, do: 1
def sign(int) when int < 0, do: -1
        
def reverse(int) when int >= 0 do
    int
    |> Integer.to_string()
    |> String.reverse()
    |> String.to_integer()
end
    
def reset_sign(int, 1), do: int
def reset_sign(int, -1), do: int * -1

def reverse_integer(int) do
    int
    |> abs()
    |> reverse()
    |> reset_sign(sign(int))
end
3 Likes

I’ve previously looked for functions akin to neg?, pos?, and zero? in the standard library but I couldn’t find anything.

I hesitate to call it an improvement :smiley:, but here’s what I came up with:

defmodule MyInt do
  defp sign_prefix(int) when int < 0, do: "-"
  defp sign_prefix(int), do: ""
  
  def reverse(int) when is_integer(int) do
    reversed_int = int
    |> Integer.to_string
    |> String.replace_leading("-", "")
    |> String.reverse
    
    "#{sign_prefix(int)}#{reversed_int}"
    |> String.to_integer
  end
end
1 Like

Have you seen :erlang.integer_to_list/1 and :erlang.list_to_integer/1?

EDIT: Elixir actually provides these through respectively Integer.to_charlist/1 and List.to_integer/1

1 Like

@Andres: I can see much simpler way:

defmodule Example do
  def sample(integer) when is_integer(integer) do
    integer |> Integer.digits() |> Enum.reverse() |> Integer.undigits()
  end
end

Is that what you wanted?

Edit: Code changed after @Ted comment

6 Likes

Wow, that is clean! :exploding_head:

I’m curious about the reason for two function heads because it seems like the first head works for both positive and negative:

iex(7)> 123 |> Integer.digits() |> Enum.reverse() |> Integer.undigits()    
321
iex(8)> -123 |> Integer.digits() |> Enum.reverse() |> Integer.undigits()
-321
1 Like

Hello @LostKobrakai

Super nice patter matching idea :smiley:

Yes! :smiley:

I’m learning so much, thanks for sharing.

Why converting to a string at all?

Just do the maths:

defmodule DR do
  def r(n), do: r(n, 0)

  def r(0, x), do: x
  def r(n, x), do: r(div(n, 10), x * 10 + rem(n, 10))
end
6 Likes

Hello @Ted

:exploding_head:, Super nice! :smiley:
I didn’t know that was possible. I’m learning so much, thanks for sharing.

Ah right, my bad - I have edited my first post

2 Likes
 -123_456_789
    |> Integer.digits() # => [-1, -2, -3, -4, -5, -6, -7, -8, -9]
    |> Enum.reverse() # => [-9, -8, -7, -6, -5, -4, -3, -2, -1]
    |> Integer.undigits() # => -987654321

But:

Integer.undigits([1, -2, 3]) # => 83

This seems a very good topic of conversation.

Hi @NobbZ
Thanks so much for sharing.

Originally I noticed this on other data too, but I found that it is not a problem in this specific case, because as far as I know Integer.digits/1 will never return list like [1, -2, 3].

There is nothing surprising with 83 as result, because we have: (3 * 10^0) + (-2 * 10^1) + (1 * 10^2) i.e. (3 * 1) + (-2 * 10) + (1 * 100) i.e. 3 - 20 + 100 which gives 83.

4 Likes

Thank you very much for clarifying it.

2 Likes