I have a collection of Option<T>
instances and want to transform from IEnumerable<Option<T>>
to Option<IEnumerable<T>>
If ALL the options have a value, then I want a Some<IEnumerable<T>>
with the collected values, if ANY of the items in the collection are None
, then I want a None<IEnumerable<T>>
This seems like it would be quite a common functional transformation, but i'm not sure if it exists in mainstream functional libraries, or what it would be called. Seems similar to FlatMap
, but not quite, as I don't want to just filter out the None
values.
I could implement it myself, but would like to find out if it exists already as a functional construct. Don't mind what language, C#, Scala, Haskell etc.
These language constructs are not initially presented in C#; however, there're multiple functional programming libraries out there for such purposes. My choice would be Paul Louth's language-ext ; the richest FP library I've found for C# so far.
There's more than one 'language family' of functional programming concepts.
In Haskell (and, apparently, Scala) this is called sequence
and is part of an abstraction or type class called Traversable ; sequence
is a special case of a traversal.
Other language famiilies exist, most notably ML. This is the where the term Option
comes from (which in Haskell is called Maybe
). While I'm well-versed in F# (which is one ML dialect), I'm not aware that traverse/sequence has an established terminology there.
Language-ext author here, you simply need to call .Sequence()
to reverse the inner and outer monads:
IEnumerable<Option<int>> items = ...;
Option<IEnumerable<int>> result = items.Sequence();
It has the exact behaviour you're looking for of returning None
if any item in the sequence is None
, and Some
otherwise.
You can also use .Traverse(...)
to map the results as you go.
As you say you don't mind the language, I have an example here using F#, which has Option<T>
out of the box:
let chooseAll source =
let anyNone = source |> Seq.exists Option.isNone
if anyNone then None else Some source
let s = [Some 1; Some 2; None]
let result1 = s |> chooseAll // None
let s = [Some 4; Some 5]
let result2 = s |> chooseAll // Some {4;5}
If you are unfamiliar with F#, Seq
is a type alias for IEnumerable<T>
. Here chooseAll
has the signature seq<'a option> -> seq<'a> option
, which translating to a C# friendly syntax is IEnumerable<Option<T>> -> Option<IEnumerable<T>>
.
You can roll your own extension method that relies on available primitives from System.Linq namespace:
public static Option<IEnumerable<T>> AllSome<T>(this IEnumerable<Option<T>> input)
where T : class
=> (input.All(o => o.IsSome)) ?
new Some<IEnumerable<T>>(input.Select(o => (o as Some<T>).Value)) :
new None<IEnumerable<T>>() as Option<IEnumerable<T>>;
This is based on a throwaway implementation of Option type that I wrote to support this example, feel free to adjust to your flavour of Option:
public class Option<T>
where T: class
{
protected T value;
public bool IsSome => value != default(T);
}
public class Some<T> : Option<T>
where T: class
{
public Some(T value) => base.value = value;
public T Value => base.value;
}
public class None<T> : Option<T>
where T: class
{
}
I played with this a bit and it seems like f# fold and foldBack might be of use in this case. Here are some samples:
let l1 = [Some 1; Some 2; None]
let l2 = [Some 3; Some 4; Some 5]
let acc (ac: int list option) le =
match ac, le with
| Some ac, Some le -> Some (le::ac)
| _, _ -> None
//this will reverse the order of elements if all present
let r1 = l1 |> List.fold acc (Some [])
let r2 = l2 |> List.fold acc (Some [])
//the order of elements preserved
let acc2 le (ac: int list option) = acc ac le
let r3 = List.foldBack acc2 l1 (Some [])
let r4 = List.foldBack acc2 l2 (Some [])
//or...
let r3b = l1 |> List.foldBack acc2 <| (Some [])
let r4b = l2 |> List.foldBack acc2 <| (Some [])
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.