Non-throwing version of Seq.exactlyOne to check the sequence of a single sequence

Update . I created a UserVoice request for this: Expand the Cardinality functions for Seq .

I need functionality Seq.exactlyOne, but with Some / None semantics. In other words, I need either Seq.head, or if the sequence is empty or contains more than one element, I do not need anything. Use Seq.exactlyOnewill cause in such cases.

I don’t think there is a built-in way to get this (although it sounds so trivial that one would expect that Seq.singletonthere is an analogue). I came up with this, but it seems confusing:

let trySingleton sq = 
    match Seq.isEmpty sq with 
    | true -> None 
    | false -> 
        match sq |> Seq.indexed |> Seq.tryFind (fst >> ((=) 1)) with
        | Some _ -> None
        | None -> Seq.exactlyOne sq |> Some

gives:

> trySingleton [|1;2;3|];;
val it : int option = None
> trySingleton Seq.empty<int>;;
val it : int option = None
> trySingleton [|1|];;
val it : int option = Some 1

? / Seq.exactlyOne, - , ( ).

UPDATE: Seq.tryItem, :

let trySingleton sq =
    match sq |> Seq.tryItem 1 with
    | Some _ -> None
    | None -> Seq.tryHead sq

(, )

+4
2

, ?

let trySingleton' (xs : seq<_>) =
    use en = xs.GetEnumerator()
    if en.MoveNext() then
        let res = en.Current
        if en.MoveNext() then None
        else Some res
    else None

trySingleton' Seq.empty<int>    // None
trySingleton' [1]               // Some 1
trySingleton' [1;2]             // None
+6

, :

let tryExactlyOne xs =
    match xs |> Seq.truncate 2 |> Seq.toList with
    | [x] -> Some x
    | _   -> None

- FSI:

> [42] |> List.toSeq |> tryExactlyOne;;
val it : int option = Some 42

> [42; 1337] |> List.toSeq |> tryExactlyOne;;
val it : int option = None

> Seq.empty<int> |> tryExactlyOne;;
val it : int option = None
+3

Source: https://habr.com/ru/post/1655412/


All Articles