Creevey
Creevey

Reputation: 43

Use list comprehension to return number of integers which contain a digit

For an assignment I have been asked to re-write the following function using list comprehension. This function takes in a list of integers and an integer digit a and returns the number of integers in the list which contain the digit a.

e.g.

count [102,41,256] 2 

Should return 2

count [98,4,376] 2

Should return 0

This is what I have for the non-list comprehension function:

split :: Int -> [Int]
split 0 = []
split x = split (div x 10) ++ [rem x 10]

checkDigits :: [Int] -> Int -> Int
checkDigits [] y = 0
checkDigits (x:xs) y
    | x == y = 1 + (checkDigits xs y)
    | otherwise = checkDigits xs y

count :: [Int] -> Int -> Int
count [] a = 0
count (x:xs) a 
    | (checkDigits (split x) a) > 0 = (checkDigits (split x) a) + (count xs a) 
    | otherwise = count xs a

Split will take in an integer and return the integer in a list

split 321    will return:    [3,2,1]

checkDigits counts the number of times that a digit appears in an integer (this was to solve an error where count [22] 2 would return as 0 when it should return 2)

e.g. the digit '2' appears 4 times in the list [2,65,42,22]

Finally in count we use checkDigits to check if any of the digits in x are equal to a, if this number is greater than 0 (1 or more are equal to a) then add the number of digits found and call again.

I am new to list comprehensions and have only managed to get it to work if the number in the list is equal to a:

countD :: [Int] -> Int -> Int
countD intL a = length [y | y <- intL, y == a]

Here countD [2,3,4,22] 2 returns 1l how do I change what I have so that it will return 3 (as there are 3 2's in the list).

Upvotes: 1

Views: 1980

Answers (3)

Ori
Ori

Reputation: 154

Best I could do. I'd appreciate if someone showed me how to pass the integer 2 digit as an argument rather than it hard coded.

Thanks Erik for your answer.

countDigits :: [Int] -> Int -> Int
countDigits xs d = sum [1 | n <- (concat . map show) xs, n == d']
    where d':[] = show d

Upvotes: 5

Erik Kaplun
Erik Kaplun

Reputation: 38247

Your question is ambiguous about what the count function should do precisely. At the beginning you say

the number of integers in the list which contain the digit

whereas later on you say

count [22] 2 /.../ should return 2

which is not the number of integers that contain the digit 2 in [22] — there is only one integer in that list, so you should return 1, regardless of the fact that this integer contains the digit 2 twice.

Therefore, I've provided solutions to both possibilities below.


For the first possibility, this solution is the shortest I could come up with:

-- these are optional
type Digit  = Int
type Number = Int
type Count  = Int

count :: [Number] -> Digit -> Count
count xs d = sum [count' x | x <- xs]  -- same as `sum $ map count' xs`
  where count' x = if d' `elem` show x then 1 else 0
        d':[] = show d

If, however, the 2nd possibility is indeed correct and count [22] 2 should return 2, then change the count' helper function accordingly:

count :: [Number] -> Digit -> Count
count xs d = sum [count' x | x <- xs]
  where count' i = length $ filter (== d') (show i)
        d':[] = show d

Upvotes: 3

Emil Vikstr&#246;m
Emil Vikstr&#246;m

Reputation: 91983

Use checkDigits and split in countD. This is how you count the number of integers that contains the digit (two, in your example):

countD intL a = length [y | y <- intL, (checkDigits (split y) a) > 0]

And here is how you count the total times the digit appears (three in your example):

countD intL a = sum [checkDigits (split y) a | y <- intL]

Upvotes: 3

Related Questions