Search code examples
.netf#seq

F# Seq.tryPick and apply function to members that were not picked


Is this possible somehow? I want to use tryPick and then continue with the "rest" of the collection.

I can only think of ugly ways to do this:

let sequence = Seq.init 5 (fun (i) -> i)
let pickedMember = 
    sequence
    |> Seq.tryPick (fun (i) ->
                        if i = 2 then
                            Some i
                        else
                            None
                    );;
let sequenceWithoutPickedMember = sequence |> Seq.filter (fun (i) -> not(i=pickedMember.Value));;

Solution

  • It is not clear from your example what you want do when Seq.tryPick returns None. I'll assume that the whole expression shall return 'None' and that the function signature chooser:('a -> 'b option) -> source:seq<'a> -> ('b * 'a list) option is acceptable for your use case.

    You'll get the option of a tuple back, containing the picked value and the rest as a list. It does not need to be a sequence because you need to consume the whole input sequence eagerly to return the result. The laziness gone, we can as well use a list.

    let tryPickWithRest chooser source =
        source
        |> Seq.fold (fun (picked, rest) x ->
            match picked, chooser x with
            | None, Some newlyPicked -> Some newlyPicked, rest
            | _ -> picked, x::rest ) (None, [])
        |> function
        | Some picked, rest -> Some(picked, List.rev rest)
        | _ -> None
    
    [0..4]
    |> tryPickWithRest (fun i -> if i = 2 then Some "found 2" else None)
    // val it : (string * int list) option = Some ("found 2", [0; 1; 3; 4])
    

    Edit

    Now that you have clarified that you want to evaluate to a tuple of the pick option and the filtered list in any case, we just borrow the idea of TheInnerLight and make it shorter. Of course, so the last will be first.

    let tryPickWithRest' chooser source =
        Seq.foldBack (fun x (picked, rest) ->
            match picked, chooser x with
            | None, Some newlyPicked -> Some newlyPicked, rest
            | _ -> picked, x::rest ) source (None, [])
    
    [-4..0]
    |> tryPickWithRest' (fun i -> if i >= 2 then Some "found 2" else None)
    // val it : string option * int list = (null, [-4; -3; -2; -1; 0])