Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default elements from a secondary source #997

Open
atifaziz opened this issue Apr 7, 2023 · 2 comments
Open

Default elements from a secondary source #997

atifaziz opened this issue Apr 7, 2023 · 2 comments

Comments

@atifaziz
Copy link
Member

atifaziz commented Apr 7, 2023

I'd like to propose an operator called Default that defaults an element from a secondary sequence when an element from the source sequence is deemed missing or faulty. A function is used to determine if an element of the source sequence is deemed missing or faulty and another function projects a result given the missing/faulty element and a default from secondary sequence for substitution/replacement.

A prototype would be as follows:

public static IEnumerable<TResult>
    Default<TSource, TDefault, TResult>(this IEnumerable<TSource> source,
                                        IEnumerable<TDefault> defaults,
                                        Func<TSource, (bool, TResult)> chooser,
                                        Func<TSource, TDefault, TResult> defaultor)
{
    using var @default = defaults.GetEnumerator();
    foreach (var item in source)
    {
        if (chooser(item) is (true, var v))
        {
            yield return v;
        }
        else
        {
            if (!@default.MoveNext())
                break;
            yield return defaultor(item, @default.Current);
        }
    }
}

The following code demonstrates the operator in action:

var source = new string?[] { "foo", null, "bar", "baz", null };
var nums = Enumerable.Range(1, int.MaxValue);

foreach (var e in source.Default(nums, s => (s is not null, s), (_, r) => $"unnamed{r}"))
    Console.WriteLine(e);

Outputs:

foo
unnamed1
bar
baz
unnamed2
@viceroypenguin
Copy link
Contributor

I would argue that as currently proposed, it is overly specific and complicated. I'd prefer to see a family of methods:

IEnumerable<TSource> DefaultIf<TSource>(this IEnumerable<TSource> source, TSource defaultValue, TSource replacementValue);
IEnumerable<TSource> DefaultIf<TSource>(this IEnumerable<TSource> source, TSource defaultValue, IEnumerable<TSource> replacementValues);
IEnumerable<TSource> DefaultIf<TSource, TDefault>(this IEnumerable<TSource> source, TSource defaultValue, IEnumerable<TDefault> replacementValues, Func<TSource, TDefault, TSource> defaultor);

Each with a matching IComparer<TSource>? comparer overload. Alternatively, Func<TSource, bool> isDefault function instead of TSource defaultValue. I think the Func<TSource, (bool, TResult)> is a pattern that creates complication and awkwardness in the code generally, but that's my opinion.

@atifaziz
Copy link
Member Author

atifaziz commented Apr 8, 2023

Those family of methods can be added as simpler overloads and which would ultimately be simple wrappers around the more generally applicable workhorse prototype I submitted. The example admittedly is a very simple one and doesn't demonstrate what's fully possible (I was in a hurry and might post a richer one later).

I think the Func<TSource, (bool, TResult)> is a pattern that creates complication and awkwardness in the code generally

The value of that function is that the output doesn't have to be tied to the source or even the default. The three types can vary as far as the algorithm is concerned. Those who don't need that flexibility can use the simpler overloads where the input and output types are the same and the missing value can be identified with an equality check as opposed to a predicate function.

Each with a matching IComparer<TSource>? comparer overload.

You mean IEqualityComparer<TSource>?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants