40
votes

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);
}
17
@FUZxxl because I want to work with each digit in turn as a number - Greg B
Something like showNumbers = show >=> return? - fuz

17 Answers

96
votes

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).

26
votes
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.

14
votes

You could also just reuse digits from Hackage.

13
votes

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?

12
votes

You can use

digits = map (`mod` 10) . reverse . takeWhile (> 0) . iterate (`div` 10)

or for reverse order

rev_digits = map (`mod` 10) . takeWhile (> 0) . iterate (`div` 10)

The iterate part generates an infinite list dividing the argument in every step by 10, so 12345 becomes [12345,1234,123,12,1,0,0..]. The takeWhile part takes only the interesting non-null part of the list. Then we reverse (if we want to) and take the last digit of each number of the list.

I used point-free style here, so you can imagine an invisible argument n on both sides of the "equation". However, if you want to write it that way, you have to substitute the top level . by $:

digits n = map(`mod` 10) $ reverse $ takeWhile (> 0) $ iterate (`div`10) n
12
votes

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))
3
votes

Via list comprehension:

import Data.Char

digits :: Integer -> [Integer]
digits n = [toInteger (digitToInt x) | x <- show n]

output:

> digits 1234567890
[1,2,3,4,5,6,7,8,9,0]
2
votes

Here's an improvement on an answer above. This avoids the extra 0 at the beginning ( Examples: [0,1,0] for 10, [0,1] for 1 ). Use pattern matching to handle cases where x < 10 differently:

toDigits :: Integer -> [Integer] -- 12 -> [1,2], 0 -> [0], 10 -> [1,0]
toDigits x
    | x < 10 = [x]
    | otherwise = toDigits (div x 10) ++ [mod x 10]

I would have put this in a reply to that answer, but I don't have the needed reputation points :(

2
votes

Applicative. Pointfree. Origami. Neat.

Enjoy:

import Data.List                                                                
import Data.Tuple                                                               
import Data.Bool                                                                
import Control.Applicative 

digits = unfoldr $ liftA2 (bool Nothing) (Just . swap . (`divMod` 10)) (> 0) 
1
votes

For returning a list of [Integer]

import Data.Char
toDigits :: Integer -> [Integer]
toDigits n = map (\x -> toInteger (digitToInt x)) (show n)
1
votes

The accepted answer is great but fails in cases of negative numbers since mod (-1) 10 evaluates to 9. If you would like this to handle negative numbers properly... which may not be the case the following code will allow for it.

digs :: Int -> [Int]
digs 0 = []
digs x
  | x < 0 = digs ((-1) * x)
  | x > 0 = digs (div x 10) ++ [mod x 10]
1
votes

I was lazy to write my custom function so I googled it and tbh I was surprised that none of the answers on this website provided a really good solution – high performance and type safe. So here it is, maybe somebody would like to use it. Basically:

  1. It is type safe - it returns a type checked non-empty list of Word8 digits (all the above solutions return a list of numbers, but it cannot happen that we get [] right?)
  2. This one is performance optimized with tail call optimization, fast concatenation and no need to do any reversing of the final values.
  3. It uses special assignment syntax which in connection to -XStrict allows Haskell to fully do strictness analysis and optimize the inner loop.

Enjoy:

{-# LANGUAGE Strict #-}

digits :: Integral a => a -> NonEmpty Word8
digits = go [] where
    go s x = loop (head :| s) tail where
        head = fromIntegral (x `mod` 10)
        tail = x `div` 10
    loop s@(r :| rs) = \case
        0 -> s
        x -> go (r : rs) x
1
votes

I've been following next steps(based on this comment):

  1. Convert the integer to a string.
  2. Iterate over the string character-by-character.
  3. Convert each character back to an integer, while appending it to the end of a list.

toDigits :: Integer -> [Integer]
toDigits a = [(read([m])::Integer) | m<-show(a)]

main = print(toDigits(1234))
0
votes

The accepted answer is correct except that it will output an empty list when input is 0, however I believe the output should be [0] when input is zero.

And I don't think it deal with the case when the input is negative. Below is my implementation, which solves the above two problems.

toDigits :: Integer -> [Integer]
toDigits n
 | n >=0 && n < 10 = [n]
 | n >= 10 = toDigits (n`div`10) ++ [n`mod`10]
 | otherwise = error "make sure your input is greater than 0" 
0
votes

I would like to improve upon the answer of Dave Clarke in this page. It boils down to using div and mod on a number and adding their results to a list, only this time it won't appear reversed, nor resort to ++ (which is slower concatenation).

toDigits :: Integer -> [Integer]

toDigits n
  | n <= 0    = []
  | otherwise = numToDigits (n `mod` 10) (n `div` 10) []
    where
      numToDigits a 0 l = (a:l)
      numToDigits a b l = numToDigits (b `mod` 10) (b `div` 10) (a:l)

This program was a solution to a problem in the CIS 194 course at UPenn that is available right here. You divide the number to find its result as an integer and the remainder as another. You pass them to a function whose third argument is an empty list. The remainder will be added to the list in case the result of division is 0. The function will be called again in case it's another number. The remainders will add in order until the end.

Note: this is for numbers, which means that zeros to the left won't count, and it will allow you to have their digits for further manipulation.

-2
votes
digits = reverse . unfoldr go
  where go = uncurry (*>) . (&&&) (guard . (>0)) (Just . swap . (`quotRem` 10))
-2
votes

I tried to keep using tail recursion

toDigits :: Integer -> [Integer]
toDigits x = reverse $ toDigitsRev x

toDigitsRev :: Integer -> [Integer]
toDigitsRev x
    | x <= 0 = []
    | otherwise = x `rem` 10 : toDigitsRev (x `quot` 10)