1

One of IEnumerable's overload is:

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector);

In the selector I wish to include the source. I know this sounds counter-intuitive because you provide the source to Select in the first place, but JavaScript has something similar. I would want to use it in a quick situation like this one here:

var greetings = new List<string> { "John", "Keith", "Sarah", "Matt" }.Select((name, index, source) => {
    if (name == source.First())
        return $"{name} (Todays Winner)";
    return name;
});

The above will have an error because Select's selector parameter does not return 3 values. Just the current object, and index. I want it to include the source.

I don't want to first create the list separately and then do .first on it.

Here is how far I've gone with the extension; I'm not sure how to implement it.

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult, IEnumerable<TSource>> selector)
{
    //not sure what to put in here, must be missing something simple ;(
}

Update

The above situation is just a made up example. My actual case requires using .Last() not .First() so index won't be useful since we don't know what the last index will be, as opposed to zero being first. Hence my need for the source to be passed back.

pnizzle
  • 6,243
  • 4
  • 52
  • 81

2 Answers2

2

This should do it:

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TSource>, TResult> selector)
{
    using (var enumerator = source.GetEnumerator()) {
        for (var i = 0 ; enumerator.MoveNext() ; i++) {
            yield return selector(enumerator.Current, i, source);
        }
    }
}

Note that you have written the wrong type for the selector parameter. It should be Func<TSource, int, IEnumerable<TSource>, TResult>, not Func<TSource, int, TResult, IEnumerable<TSource>>.

If you just want to check if an element is the first, why not just check index == 0?

var greetings = new List<string> { "John", "Keith", "Sarah", "Matt" }.Select((name, index, source) => {
    if (index == 0)
        return $"{name} (Todays Winner)";
    return name;
});
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • You are right with check index zero, but my real code needs Last(). Should have used Last in my example. Let me try using yield. First time using that. – pnizzle Sep 24 '19 at 08:41
  • And yes, of coarse, TResult comes last as the return type haha. Cheers. – pnizzle Sep 24 '19 at 08:47
  • Is it possible to something similar with `foreach`? That is, an extension that somehow passes back the source on each iteration – pnizzle Sep 24 '19 at 22:46
  • @pnizzle you mean something like the approach shown in the other answer? – Sweeper Sep 25 '19 at 05:10
  • No. I mean instead of using `.select` I use `foreach(var (x, source) in new List {"george", "keith", "sarah"})`. Something like that. I looked into it a bit. Seems `foreach` is not even defined as function. Cant even do `go to definition` on it. It can not be overloaded. FYI I ended up using the approach in the someBody's answer. But you answered first and have more useful detail in your answer, hence marked as accepted. – pnizzle Sep 25 '19 at 05:13
  • @pnizzle don’t you already have access to the source in the foreach loop? You can just use `myList`. That _is_ your source, isn’t it? – Sweeper Sep 25 '19 at 05:17
  • You are right, check my edit on previous comment. I want to define the static list right within the `foreach` and be able to reference it somehow. I know this all seems trivial/unnecessary but I'm just curious. Using select is a problem because calling other methods inside select is not possible (lambda expression error blah blah) hence why i want foreach. I dont need this, just curious :) – pnizzle Sep 25 '19 at 05:19
  • 1
    @pnizzle After some experimenting, I found that you need to use a custom list class to do this. You just need to make that list implement `IEnumerable<(T, CustomList)>`. – Sweeper Sep 25 '19 at 05:42
2

This should work:

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TSource>, TResult> selector)
{
        int index = 0;
        foreach(var item in source)
        {
            yield return selector(item, index, source);
            index++;   
        }
}
SomeBody
  • 7,515
  • 2
  • 17
  • 33