Replies: 6 comments 2 replies
-
Since tuple deconstruction already works in The code would look something like this: public static class Enumerable
{
public static IEnumerable<(T item, int index)> Indexed<T>(this IEnumerable<T> source)
{
int i = 0;
foreach (var item in source)
{
yield return (item, i);
i++;
}
}
}
…
foreach (var (item, i) in x.Indexed())
{
Console.WriteLine($"Item {i}: {item}");
} The performance would not be as good as doing this in the language, but I think this is a convenience feature, so performance is not that important. |
Beta Was this translation helpful? Give feedback.
-
Haha, just seeing this now. I called mine |
Beta Was this translation helpful? Give feedback.
-
It was inevitable that many folk would create such an extension method, I guess. I added foreach (var (index, value) = collection.Indexed())
{
...
} This is something that could be added to the BCL, but adding special language support seems unnecessary. |
Beta Was this translation helpful? Give feedback.
-
I agree with the idea for something like |
Beta Was this translation helpful? Give feedback.
-
It would be nice to have foreach with index syntax so it wouldn’t require creating lambda:
|
Beta Was this translation helpful? Give feedback.
-
It did seem we have this in dotnet 9 https://learn.microsoft.com/en-gb/dotnet/core/whats-new/dotnet-9/overview#linq |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
@johndog commented on Thu Sep 15 2016
Problem
Consider a simple foreach example (Example 1):
Suppose I want to include the iteration count--i.e the item index--in the console message. If I maintain the use of foreach, the following is a typical pattern that results (Example 2):
Not only is this pattern tedious to do correctly, there is also a tendency to leak the variable "i" to the encompassing scope, a broader context than the one in which it is needed. It requires precise attention to detail to recognize the need for the surrounding block whose sole purpose is to contain the scope of the i variable.
Sometimes this scenario can be a motivating factor in converting to the for(int i= 0 ;; ++i) syntax, but that approach becomes impractical if the collection doesn't support random access.
Proposed Solution
I propose a new compile-time keyword operator, here dubbed 'indexof', which would be used as follows (Example 3):
This would compile to code which is equivalent to the output of Example 2. It would eliminate a few classes of bugs, and make the code more readable.
Nesting
This also comes in handy for nesting scenarios (Example 4):
This example eliminates two temporary variables, and two false decouplings between counter and iterator. For example, it would be impossible to swap i and j indexes inadvertently.
Stretch: Linq
I imagine it to be more challenging to implement, but it would be no less useful in Linq (Example 5):
Stretch: Contextually Inferred Operand
We could enable a brazen and robust copy/paste by permitting an inferred operand syntax. In the case following, indexof() is given no parameter. Instead, it would be expected to count the number of times it enters the current scope, within the lifetime of the surrounding scope. This is essentially identical to the behavior of Example 3, illustrating that the only reason the index(item) form is needed is in the case of ambiguity. (Example 6):
This would make it so that some expressions were valid in any loop, without adapting to the names of specific variables. (Copy/paste friendly, minimal redundancy).
Alternative Operator Names
Other names I've considered:
@HaloFour commented on Thu Sep 15 2016
This is just a variant of #8130 which had a better syntax than this, but was still closed as "Won't Fix". Something like an
indexof
is confusing because it implies that it knows the index of the iterated from the original array/collection which is certainly not the case given that an enumerator can enumerate in way it wishes.As for "LINQ", that would just be an extension method which you can author yourself right now:
@AdamSpeight2008 commented on Thu Sep 15 2016
LINQ already supports this.
Select( ( item, index ) => )
@whoisj commented on Thu Sep 15 2016
It would be nice to be able to stop writing:
... but adding "secret" values seems, confusing. @HaloFour suggested a very workable solution, which only needs to be added once per project and does resolve the problem.
And with value tuples, it could be taken further to something like:
@ufcpp commented on Thu Sep 15 2016
Some other languages like Go have tuple-based syntax for this purpose. If we import this syntax to C#, it will be like:
However, is it enough to have an extension method with tuples as follows?
@mburbea commented on Thu Sep 15 2016
with tuples you can already do this pretty trivially...
@dsaf commented on Fri Sep 16 2016
@ufcpp You just described the Swift approach it seems :):
http://stackoverflow.com/questions/24028421/swift-for-loop-for-index-element-in-array
@johndog commented on Sat Sep 17 2016
I took a crack at some of the ideas mentioned with the capabilities of C# 6, with this result:
Enumerated() looks like this:
I guess I don't hate this. The main problem is that it requires me to name an entity I don't want to name (nth item), and doesn't permit me to name the one entity I do want to name (the elements returned from GetFibonacciSequence(), and which I have to refer to as nthItem.Item).
It looks like the tuples support in C#7 might change this, allowing creation of an Enumerate() extension method that returns a tuple with good names of my choosing. It's better since I can now name the thing I care about, but I don't like that I still have to pick a name for the index, and that the choice is decoupled from the item itself or from the concept of a loop scope counter. It shares the namespace with other variables, so I might have to pick a name for it, and while still making sure the index name stays consistent with the iterated name.
That's the problem that indexof(n) is trying to solve: it's coupled so precisely to its idea, and the relationship of that idea to the iteration variable it is used with, that you can't give it it's own name, and you'd never want to. I can rename n and change its meaning, but I can't change the meaning of indexof(n), so I want neither the flexibility nor the responsibility to name it. Name conflicts with n might be possible, be never with the derived index.
@Thaina commented on Tue Sep 27 2016
Well, after there is tuple it is as easy as
@johndog commented on Wed Sep 28 2016
@Thaina - For the stated goal, I find that too verbose. That said, I don't see any reason your .Select(...) call couldn't be extracted into a simple .Enumerate() extension method, as I suggested in my prior post. In that post I also visited the pros/cons of the tuples syntax.
@ashmind commented on Wed Oct 19 2016
Just to note that performance of
does not match performance of
foreach
for arrays, since array foreach gets compiled tofor
and doesn't useIEnumerable
at all.I definitely vote for some kind of index with no overhead other than one local variable.
Beta Was this translation helpful? Give feedback.
All reactions