How to iterate "between" elements in an array / collection / list?

This problem has bothered me for many years, and it always seems to me that I come up with a hack when there is a much better solution. The problem arises when you want to do something to all elements of a list, and then add something between these elements. In short, I want:

  • Do something for everyone on the list.
  • Do something else for everything except the last item in the list (in fact, do something β€œin between” in the list).

For example, suppose I have a class called Equation :

 public class Equation { public string LeftSide { get; set; } public string Operator { get; set; } public string RightSide { get; set; } } 

I want to Equation over the Equation list and return a string that formats these elements together; something like the following:

 public string FormatEquationList(List<Equation> listEquations) { string output = string.Empty; foreach (Equation e in listEquations) { //format the Equation string equation = "(" + e.LeftSide + e.Operator + e.RightSide + ")"; //format the "inbetween" part string inbetween = " and "; //concatenate the Equation and "inbetween" part to the output output += equation + inbetween; } return ouput; } 

The problem with the above code is that it will include and at the end of the returned string. I know that I could knock the code together, replace the foreach for loop, and add an inbetween element only if it is not the last element; but it looks like a hack.

Is there a standard methodology to solve this problem?

+4
source share
7 answers

Basically, you have several different strategies to solve this problem:

  • Process the first (or last) element outside the loop.
  • Do the work, and then undo the extraneous step.
  • Determine that you are processing the first or last element inside the loop.
  • Use a higher level abstraction that avoids the situation.

Any of these parameters can be a legitimate way to implement the "between elements" style of the algorithm. Which one you choose depends on such things as:

  • what style do you like
  • how expensive is the "cancel the job"
  • how expensive is the step to "join"
  • are there any side effects

Among other things. For the particular case of the string, I personally prefer to use string.Join() , as I find it most clearly illustrates the intention. Also, in the case of strings, if you are not using string.Join() , you should try to use StringBuilder to avoid creating too many temporary strings (a consequence of immutable strings in .Net).

As an example, we use string concatenation; various variants are divided into examples as follows. (For simplicity, suppose the equation has ToString() as: "(" + LeftSide + Operator + RightSide + ")"

 public string FormatEquation( IEnumerable<Equation> listEquations ) { StringBuilder sb = new StringBuilder(); if( listEquations.Count > 0 ) sb.Append( listEquations[0].ToString() ); for( int i = 1; i < listEquations.Count; i++ ) sb.Append( " and " + listEquations[i].ToString() ); return sb.ToString(); } 

The second option looks like this:

 public string FormatEquation( IEnumerable<Equation> listEquations ) { StringBuilder sb = new StringBuilder(); const string separator = " and "; foreach( var eq in listEquations ) sb.Append( eq.ToString() + separator ); if( listEquations.Count > 1 ) sb.Remove( sb.Length, separator.Length ); } 

The third will look something like this:

 public string FormatEquation( IEnumerable<Equation> listEquations ) { StringBuilder sb = new StringBuilder(); const string separator = " and "; foreach( var eq in listEquations ) { sb.Append( eq.ToString() ); if( index == list.Equations.Count-1 ) break; sb.Append( separator ); } } 

The last option can take several forms in .NET using either String.Join or Linq:

 public string FormatEquation( IEnumerable<Equation> listEquations ) { return string.Join( " and ", listEquations.Select( eq => eq.ToString() ).ToArray() ); } 

or

 public string FormatEquation( IEnumerable<Equation> listEquations ) { return listEquations.Aggregate((a, b) => a.ToString() + " and " + b.ToString() ); } 

Personally, I don't use Aggregate() to concatenate strings, because this leads to a lot of intermediate, discarded strings. This is also not the most obvious way to "combine" a set of results together - it is primarily intended to calculate the "scalar" results from the collection in some arbitrary way defined by the caller.

+8
source

You can use String.Join () .

 String.Join(" and ",listEquations.Select(e=>String.Format("({0}{1}{2})",e.LeftSide,e.Operator,e.RightSide).ToArray()); 
+6
source

You can do this with the LINQ Aggregate statement:

 public string FormatEquationList(List<Equation> listEquations) { return listEquations.Aggregate((a, b) => "(" + a.LeftSide + a.Operator + a.RightSide + ") and (" + b.LeftSide + b.Operator + b.RightSide + ")"); } 
+3
source

Using a for loop with a counter is quite reasonable if you don't want a foreach . That is why there is more than one type of cycle.

If you want to process elements in pairs, loop on LINQ Aggregate .

+2
source

Usually I add it before the condition and check if there is its first element.

 public string FormatEquationList(List<Equation> listEquations) { string output = string.Empty; foreach (Equation e in listEquations) { //use conditional to insert your "between" data: output += (output == String.Empty) ? string.Empty : " and "; //format the Equation output += "(" + e.LeftSide + e.Operator + e.RightSide + ")"; } return ouput; } 

I have to say that I will look at the function string.Join (), +1 for Linqiness on this. My example is a more traditional solution.

+2
source

I usually try to use prefix delimiters based on a condition, rather than adding them to the end.

 string output = string.Empty; for (int i = 0; i < 10; i++) { output += output == string.Empty ? i.ToString() : " and " + i.ToString(); } 

0 and 1 and 2 and 3 and 4 and 5 and 6 and 7 and 8 and 9

+1
source

I like the String.Join method already published.

But when you are not using Array, this was usually my solution to this problem:

 public string FormatEquationList(List<Equation> listEquations) { string output = string.Empty; foreach (Equation e in listEquations) { // only append " and " when there something to append to if (output != string.Empty) output += " and "; output += "(" + e.LeftSide + e.Operator + e.RightSide + ")"; } return output; } 

Of course, it is usually faster to use StringBuilder:

 public string FormatEquationList(List<Equation> listEquations) { StringBuilder output = new StringBuilder(); foreach (Equation e in listEquations) { // only append " and " when there something to append to if (output.Length > 0) output.Append(" and "); output.Append("("); output.Append(e.LeftSide); output.Append(e.Operator); output.Append(e.RightSide); output.Append(")"); } return output.ToString(); } 
+1
source

Source: https://habr.com/ru/post/1300780/


All Articles