How to List.sum a List (Maybe Float)?

Viewed 196

I am trying to convert strings to floats and find their sum:

> String.split "," "0.2,0.3,3.1" 
> List.map String.toFloat ["0.2","0.3","3.1"]
> List.sum [Just 0.2,Just 0.3,Just 3.1]

But I'm getting these compiler messages:

This argument is a list of type:

    List (Maybe Float)

But `sum` needs the 1st argument to be:

    List number

Hint: Use Maybe.withDefault to handle possible errors. Longer term, it is
usually better to write out the full `case` though!

How can I get the sum of these values?

4 Answers

You have created a List (Maybe Float), but you need a List Float (number is a special type which can be either Int or Float). This means that you need to handle the case when the value in the list is Nothing. The error message suggests using Maybe.withDefault, which would look something like this:

"0.2,0.3,3.1"
|> String.split ","
|> List.map String.toFloat
|> List.map (Maybe.withDefault 0)
|> List.sum

Alternatively, List.filterMap is designed to take a function which returns a Maybe and remove Nothing values. In this case, String.toFloat is that kind of function, so you could to this instead:

"0.2,0.3,3.1"
|> String.split ","
|> List.filterMap String.toFloat
|> List.sum

As the error message points out, you need to handle the Nothing case. Assuming you simply want to discard those values, you can run your list through List.filterMap.

> l = [Just 0.2,Just 0.3,Just 3.1,Nothing]
[Just 0.2,Just 0.3,Just 3.1]
    : List (Maybe Float)
> List.filterMap identity l |> List.sum
3.6 : Float

You can compose a function to reuse your logic:

> justSum = List.filterMap identity >> List.sum
<function> : List (Maybe number) -> number
> justSum l
3.6 : Float

String.toFloat returns Nothing when the string is not a valid float. You need to decide what happens when you have a string like this:

"0.2,0.3,3.1,h"

Because it contains h which is not a number, running String.split "," then List.map String.toFloat will produce this result:

[Just 0.2, Just 0.3, Just 3.1, Nothing]

Other answers suggest that you use List.filterMap to ignore the invalid strings. But if you instead want the sum to be Nothing if any strings are invalid, you can use Maybe.Extra.combine like this:

import Maybe.Extra

"0.2,0.3,3.1"
  |> String.split ","
  |> List.map String.toFloat
  |> Maybe.Extra.combine
  |> Maybe.map List.sum

You can use the following function:

sumMaybes : List (Maybe number) -> Maybe number
sumMaybes = List.foldl (Maybe.map2 (+)) (Just 0)

-- examples

sumMaybes [Just 1, Just 2] == Just 3
sumMaybes [Nothing, Just 2] == Nothing
sumMaybes [] = Just 0
Related