Is there an equivalent to Python's enumerate() for .NET IEnumerable
I could not find a related question.
In python
you can easily loop through a sequence (list
, generator
etc) and collect the index of the iteration at the same time thanks to enumerate(seq)
like this :
>>> for (i,item) in enumerate(["toto","titi","tutu"]):
... print i, item
...
0 toto
1 titi
2 tutu
Is there something similar for IEnumerable, that would, for instance, transform a IEnumerable<T>
in a IEnumerable<Tuple<Int32,T>>
?
(I know it would be easily done thanks to the correct function in Select()
.. but if it exists, I'd rather use it :) )
UPDATE FYI, I开发者_如何转开发 am curious about this kind of possibility to be able to do something like : "give me the index of the last item that fulfils this condition", which would then be accomplished through :
myEnumeration.First(t => some condition on t.Item2 ... ).Item1;
C# 7 finally allows you to do this in an elegant way:
static class Extensions
{
public static IEnumerable<(int, T)> Enumerate<T>(
this IEnumerable<T> input,
int start = 0
)
{
int i = start;
foreach (var t in input)
yield return (i++, t);
}
}
class Program
{
static void Main(string[] args)
{
var s = new string[]
{
"Alpha",
"Bravo",
"Charlie",
"Delta"
};
foreach (var (i, o) in s.Enumerate())
Console.WriteLine($"{i}: {o}");
}
}
Instead of using a Tuple<,>
(which is a class) you can use a KeyValuePair<,>
which is a struct. This will avoid memory allocations when enumerated (not that they are very expensive, but still).
public static IEnumerable<KeyValuePair<int, T>> Enumerate<T>(this IEnumerable<T> items) {
return items.Select((item, key) => new KeyValuePair(key, item));
}
As for a specific function that will do what you're asking, I don't know if .NET includes it. The quickest way, however, would just be to do something like this:
int id = 0;
foreach(var elem in someList)
{
... doStuff ...
id++;
}
EDIT: Here is a function that will do as you ask, using yield return
, but it has the downside of requiring one GC allocation per iteration:
public static IEnumerable<Tuple<int, T>> Enumerate<T>(IEnumerable<T> list)
{
int id = 0;
foreach(var elem in list)
{
yield return new Tuple<int, T>(id, elem);
id++;
}
}
Here is my own answer to my own question ...
If it does not exist, I might as well do it like that, without actually writing a for
/foreach
:
var items = new List<String> { "toto", "titi", "tutu" };
var enumerated = items.Select((x, i) => new Tuple<int, String>(i, x));
foreach (var t in enumerated)
{
Console.WriteLine(String.Format("{0} : {1}", t.Item1, t.Item2));
}
which prints:
0 : toto
1 : titi
2 : tutu
It's a one-liner ... an ugly one, but a one-liner anyway :)
Here's an extension method, that will return sequence of tuples, where the first item is number in a sequence and the second item is the value from the source sequence.
public static IEnumerable<Tuple<int, T>> Enumerate<T>(this IEnumerable<T> source, int start = 0)
{
return source.Select((value, index) => new Tuple<int, T>(index + start, value));
}
Examples:
var source = new[] { "apple", "pear", "banana", "orange", "lemon" };
var enumerated1 = source.Enumerate(); // { (0, "apple"), (1, "pear"), (2, "banana"), (3, "orange"), (4, "lemon") }
var enumerated2 = source.Enumerate(3); // { (3, "apple"), (4, "pear"), (5, "banana"), (6, "orange"), (7, "lemon") }
How about using Zip
with Enumerable.Range
?
var list = new List<string> { "toto", "titi", "tutu" };
foreach (var (str, i) in list.Zip(Enumerable.Range(0, int.MaxValue)))
{
Console.WriteLine(i + " " + str);
}
Output:
0 toto
1 titi
2 tutu
精彩评论