Update: I created a UserVoice request for this: Expand on the Cardinality functions for Seq.
I need the functionality of 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 item, I need nothing. Using Seq.exactlyOne
will throw in such cases.
I don't think there's a built-in way of getting this (though it sounds so trivial that one would expect there is a counterpart for Seq.singleton
). I came up with this, but it feels convoluted:
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
Is there a simpler, or even a built-in way? I could try/catch on Seq.exactlyOne
, but that is building business logic around exceptions, I'd rather not (and it is expensive).
UPDATE:
I wasn't aware of the Seq.tryItem
function, which would make this simpler:
let trySingleton sq =
match sq |> Seq.tryItem 1 with
| Some _ -> None
| None -> Seq.tryHead sq
(better, but it still feels rather awkward)
Why not approach the problem by handling the enumerator imperatively?
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
I'm not aware of a built-in function for this, but here's an alternative way to implement it:
let tryExactlyOne xs =
match xs |> Seq.truncate 2 |> Seq.toList with
| [x] -> Some x
| _ -> None
FSI demo:
> [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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With