ca9163d9
ca9163d9

Reputation: 29159

Filter and convert `list option` to `list`?

I have the following code which will return a seq of DownloadLink for these Urls that can be parsed.

type DownloadLink = { Url: string; Period: DateTime }   

nodes |> Seq.map (fun n ->
    let url = n.Attributes.["href"].Value
    match url with
    | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
        { Url = url; Period = period }
    | _ ->
        printfn "Cannot parse %s" url // Error
        )

However, I got the following error at the printfn. What's right way to implement it? Should I make it a list option first and then filter out these None items?

Error   1   Type mismatch. Expecting a
    string -> DownloadLink    
but given a
    string -> unit    
The type 'DownloadLink' does not match the type 'unit'  

Upvotes: 3

Views: 1107

Answers (2)

Tomas Petricek
Tomas Petricek

Reputation: 243041

Aside from Seq.choose, you can also nicely solve the problem using sequence expressions - where you can use yield to return result in one branch, but do not have to produce a value in another branch:

seq { for n in nodes do
        let url = n.Attributes.["href"].Value
        match url with
        | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
            yield { Url = url; Period = period }
        | _ ->
            printfn "Cannot parse %s" url }

Aside, I would not recommend doing a side effect (printing) as part of your processing code. If you want to report errors, it might be better to return an option (or define a type which is either Success or Error of string) so that the error reporting is separated from processing.

Upvotes: 5

John Palmer
John Palmer

Reputation: 25516

The basic problem is that if you have something like

match x with
|true -> A
|false -> B

the type of A and B must be the same.

There is actually a build in function that combines the map and filter using Some that you had though of - use Seq.choose like so

nodes |> Seq.choose (fun n ->
    let url = n.Attributes.["href"].Value
    match url with
    | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
        Some ({ Url = url; Period = period })
    | _ ->
        printfn "Cannot parse %s" url // Error
        None
        )

Upvotes: 6

Related Questions