patterncsharpMinor
Concise code to perform action on every element, when need index
Viewed 0 times
needeveryactionconciseperformelementwhencodeindex
Problem
When I read this answer, which has code:
My initial reaction was similar to Mark Sowul's comment:
"Wastes memory (allocating a list or array) and risks bugs (if you don't call ToList or ToArray). Why?".
But on further thought, what is a concise approach, avoiding the downsides of the above?
My first attempt:
Compared to calling a Linq operator, that seems verbose, low-level. I'm defining and manipulating an index, whereas Linq has an Operator that "almost" does what I want - takes care of the index tracking for me.
Personally, I would write the explicit
Is there a more concise solution, that doesn't have the downsides of the first version shown (Where / ToArray)?
I'm trying to get comfortable with encapsulating such stuff into methods, often by using
As an aside, Linq is also newer than my habits. Linq is concise, but not in a way that I find helpful; I find loops clearer to read; a better separation of the aspects of what is happening. But I'm open to seeing situations where its expressive power is appropriate.
[OFF-TOPIC: In contrast, I spent some time with F#, and found it to be fantastic. I LOVE the combination of functional with OO; just not the way Linq jams a handful of functional constructs into C#'s syntax. But this question is about making good use of C#, so all advice is appreciated.]
I think this becomes:
```
public static void ForEachWithInd
linqObject.Where((obj, index) => {
DoWork(obj, index);
return true;
}).ToArray(); //MUST CALL ToArray() or ToList() or something to execute the lazy query, or the loop won't actually executeMy initial reaction was similar to Mark Sowul's comment:
"Wastes memory (allocating a list or array) and risks bugs (if you don't call ToList or ToArray). Why?".
But on further thought, what is a concise approach, avoiding the downsides of the above?
My first attempt:
int index = 0;
foreach (var item in someEnumerable) {
DoWork(item, index);
index++;
}Compared to calling a Linq operator, that seems verbose, low-level. I'm defining and manipulating an index, whereas Linq has an Operator that "almost" does what I want - takes care of the index tracking for me.
Personally, I would write the explicit
foreach loop - but maybe that is only because I've been programming a long time, and am comfortable with that syntax.Is there a more concise solution, that doesn't have the downsides of the first version shown (Where / ToArray)?
I'm trying to get comfortable with encapsulating such stuff into methods, often by using
Action<> or Func<>, which are newer [to me] than the style of programming that became my habit. So that instead of repeating similar snippets of code, I just call a method.As an aside, Linq is also newer than my habits. Linq is concise, but not in a way that I find helpful; I find loops clearer to read; a better separation of the aspects of what is happening. But I'm open to seeing situations where its expressive power is appropriate.
[OFF-TOPIC: In contrast, I spent some time with F#, and found it to be fantastic. I LOVE the combination of functional with OO; just not the way Linq jams a handful of functional constructs into C#'s syntax. But this question is about making good use of C#, so all advice is appreciated.]
I think this becomes:
```
public static void ForEachWithInd
Solution
linqObject.Where((obj, index) => {
DoWork(obj, index);
return true;
}).ToArray();Never ever! This is the worst possible abuse of an API. I 100% agree with the comment.
int index = 0;
foreach (var item in someEnumerable) {
DoWork(item, index);
index++;
}Yes, this is the way it should be, although this can be shortened... keep reading.
public static void ForEachWithIndex(IEnumerable e, Action a)
{
int index = 0;
foreach (T v in e) {
a(index, v);
index++;
}
}This is better but the naming is still quite wrong and the
Action parameters are in the wrong order. They should be incremental. This means that T is always there so it should be first, then any other parameters may follow, like the index.I'm not sure what's wrong with using normal loops where necessary but if you really want to encapsulate them then...
Try to be consistant with the LINQ API and don't invent new method suffixes like
WithIndex. Use overloads instead. In fact you can implement it once and call the core method with other overloads.Notice the order of
Action parameters. It's consistant with current APIs. T first then index. There is also no explicit parameter checking because it uses an empty collection if the source is null and the ? operator to not call the delegate if this one is null. You can also throw if you want.public static class Loop
{
public static void ForEach(IEnumerable source, Action action)
{
var i = 0;
foreach (var item in source ?? Enumerable.Empty()) action?.Invoke(item, i++);
}
public static void ForEach(IEnumerable source, Action action)
{
ForEach(source, (x, i) => action(x));
}
}Code Snippets
linqObject.Where((obj, index) => {
DoWork(obj, index);
return true;
}).ToArray();int index = 0;
foreach (var item in someEnumerable) {
DoWork(item, index);
index++;
}public static void ForEachWithIndex<T>(IEnumerable<T> e, Action<int, T> a)
{
int index = 0;
foreach (T v in e) {
a(index, v);
index++;
}
}public static class Loop
{
public static void ForEach<T>(IEnumerable<T> source, Action<T, int> action)
{
var i = 0;
foreach (var item in source ?? Enumerable.Empty<T>()) action?.Invoke(item, i++);
}
public static void ForEach<T>(IEnumerable<T> source, Action<T> action)
{
ForEach(source, (x, i) => action(x));
}
}Context
StackExchange Code Review Q#156783, answer score: 7
Revisions (0)
No revisions yet.