简体   繁体   中英

F# Array.choose id equivalent in C#

I love using Array.choose id in F# on Option[]. What's the best way to do this in C# for Nullable[]?

--- Edited to address excellent comments ---
In F#, I use Array.choose id to filter out None : Option<'T>[] -> 'T [] .
What's a good way to filter out null in C# with Linq: Nullable<T>[] => T [] ?

I would translate it into this in C#

int?[] all = new int?[3] { 10, null, 100 };
int[] chosen = all
    .Where(e => e.HasValue)
    .Select(e => e.Value)
    .ToArray();

You can omit ToArray() if you just want is as an IEnumerable, and .Select(..) if you don't want the nullables unpacked

You can use SelectMany given another function to convert a T? into an IEnumerable<T> :

public static IEnumerable<T> ToSeq<T>(this T? v) where T : struct
{
    if (v.HasValue)
    {
        yield return v.Value;
    }
}

then:

var nullables = new int?[] { null, 1, 4, null, 3, null, 29 };
int[] values = nullables.SelectMany(ni => ni.ToSeq()).ToArray();

If you're a fan of the F# Option, you might like the Optional library in C#.

In any case, I like using an extension method for this. I use it with that Optional library, but here's a Nullable version. This version is restricted to outputting value types, but the Option version can also handle reference types.

/// <summary>
///     Allows you to map and filter in a single operation, by passing in a function that returns
///     a Nullable containing the output that should be included in the final result.
///     Only the values that are not null are included in the resulting sequence.
/// </summary>
public static IEnumerable<T2> Choose<T1, T2>(this IEnumerable<T1> enumerable, Func<T1, T2?> selector) where T2 : struct
{
    if (enumerable is null) throw new ArgumentNullException(nameof(enumerable));
    if (selector is null) throw new ArgumentNullException(nameof(selector));

    // The nested function ensures argument validation happens immediately, rather than
    // being delayed until the caller starts iterating the results.

    IEnumerable<T2> iterator()
    {
        foreach (var item in enumerable)
        {
            var output = selector(item);
            if (output.HasValue)
                yield return output.Value;
        }
    }

    return iterator();
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM