Split a number into its digits with Haskell

56,117

Solution 1

Have you heard of div and mod?

You'll probably want to reverse the list of numbers if you want to treat the most significant digit first. Converting the number into a string is an impaired way of doing things.

135 `div` 10 = 13
135 `mod` 10 = 5

Generalize into a function:

digs :: Integral x => x -> [x]
digs 0 = []
digs x = digs (x `div` 10) ++ [x `mod` 10]

Or in reverse:

digs :: Integral x => x -> [x]
digs 0 = []
digs x = x `mod` 10 : digs (x `div` 10)

This treats 0 as having no digits. A simple wrapper function can deal with that special case if you want to.

Note that this solution does not work for negative numbers (the input x must be integral, i.e. a whole number).

Solution 2

digits :: Integer -> [Int]
digits = map (read . (:[])) . show

or you can return it into []:

digits :: Integer -> [Int]
digits = map (read . return) . show

or, with Data.Char.digitToInt:

digits :: Integer -> [Int]
digits = map digitToInt . show

the same as Daniel's really, but point free and uses Int, because a digit shouldn't really exceed maxBound :: Int.

Solution 3

You could also just reuse digits from Hackage.

Solution 4

Textbook unfold

import qualified Data.List as L
digits = reverse . L.unfoldr (\x -> if x == 0 then Nothing else Just (mod x 10, div x 10))

Solution 5

Using the same technique used in your post, you can do:

digits :: Integer -> [Int]
digits n = map (\x -> read [x] :: Int) (show n)

See it in action:

Prelude> digits 123
[1,2,3]

Does that help?

Share:
56,117

Related videos on Youtube

Greg B
Author by

Greg B

I own Backslash Software, a small digital marketing agency. I was head of .NET development at twentysix. I have won a Flash Forward award (in the art category) for my work on 15x15. I am an EPiServer certified developer.

Updated on May 24, 2020

Comments

  • Greg B
    Greg B over 3 years

    Given an arbitrary number, how can I process each digit of the number individually?

    Edit I've added a basic example of the kind of thing Foo might do.

    For example, in C# I might do something like this:

    static void Main(string[] args)
    {
        int number = 1234567890;
        string numberAsString = number.ToString();
    
        foreach(char x in numberAsString)
        {
            string y = x.ToString();
            int z = int.Parse(y);
            Foo(z);
        }
    }
    
    void Foo(int n)
    {
        Console.WriteLine(n*n);
    }
    
    • kennytm
      kennytm about 13 years
    • Greg B
      Greg B about 13 years
      @FUZxxl because I want to work with each digit in turn as a number
    • fuz
      fuz about 13 years
      Something like showNumbers = show >=> return?
  • kennytm
    kennytm about 13 years
  • Greg B
    Greg B about 13 years
    I've added an example to my code as I don't see how div and mod will help me walk over the digits of any arbitrary number. Could you expand on your thoughts please.
  • Roman Gonzalez
    Roman Gonzalez about 13 years
    @Greg B this is a haskell source code that does the exact same thing your algorithm does, but using @supercooldave algorithm => pastie.org/1231091
  • muhmuhten
    muhmuhten about 13 years
    the digitToInt version is probably better anyway, and :[] was slightly more obvious to me. eh, I'll edit it in. I have no idea where pure is from, so.
  • muhmuhten
    muhmuhten about 9 years
    reread this, this time recognizing pure and yes, that would be equivalent. (it is required to be equivalent.)
  • MySchizoBuddy
    MySchizoBuddy almost 9 years
    can you explain what the "period" does between read and return
  • muhmuhten
    muhmuhten almost 9 years
    function composition. (.) :: (b -> c) -> (a -> b) -> a -> c
  • MySchizoBuddy
    MySchizoBuddy almost 9 years
    so that digs 001 will be [0,0,1]
  • MySchizoBuddy
    MySchizoBuddy almost 9 years
    digits 0123 should be [0,1,2,3]
  • Dave Clarke
    Dave Clarke almost 9 years
    digs 001 = digs 1 = [1] because 001 = 1.
  • alice kibin
    alice kibin about 8 years
    Or in point free style it would be: toDigits = map (toInteger . digitToInt) . show
  • eatingthenight
    eatingthenight about 8 years
    Just a reminder that this fails in the case of negative numbers.
  • Isaac
    Isaac over 7 years
    This answer would be much improved with an explanation
  • Raman Shah
    Raman Shah over 6 years
    Could you refactor that with an abs?
  • Tamoghna Chowdhury
    Tamoghna Chowdhury over 6 years
    This isn't tail recursion - there's a cons operation in the last branch other than the recursive call.
  • Tamoghna Chowdhury
    Tamoghna Chowdhury over 6 years
    This isn't tail recursion - there's a cons operation in the last branch other than the recursive call.
  • Redu
    Redu about 6 years
    Obviously this is the way to do. If you include import Data.Bool.bool then you can make it even more sexy like unfoldr (\x -> bool Nothing (Just (rem x 10, div x 10)) (x > 0))
  • Blank Chisui
    Blank Chisui about 6 years
    quotRem splits of the last digit basically and returns a tuple of the digit and the rest.
  • Matthias Braun
    Matthias Braun over 5 years
    Variant that works with negative numbers: digits d = reverse . unfoldr (\x -> bool (Just $ swap $ divMod x 10) Nothing (x == 0)) $ abs d. Needed imports: Data.List (unfoldr), Data.Tuple (swap), Data.Bool (bool)
  • dopamane
    dopamane about 5 years
    I like how this solution uses map versus pattern matching, such idiom!
  • jpmarinier
    jpmarinier over 4 years
    Perfect solution, except that it is probably best to use divMod, if we are not sure whether the compiler will optimize the common work between div and mod all by itself.
  • Wilco Verhoef
    Wilco Verhoef almost 3 years
    @MySchizoBuddy No, 0123 isn't an Integer. If you were to use that literal in your code it would just be interpreted as the value 123 or 123.0 depending on the type. Try typing just 0123 in ghci.