简体   繁体   English

如何迭代数组/集合/列表中的“之间”项?

[英]How do I iterate “between” items in an array / collection / list?

This problem has bugged me for years, and I always feel like I'm coming up with a hack when there's a much better solution. 多年来这个问题一直困扰着我,而且当我有一个更好的解决方案时,我总觉得我想要一个黑客。 The issue at hand occurs when you want to do something to all items in a list and then add something inbetween those items. 当你想要做的事,以所有列表中的项目,然后添加一些插图中的项目出现在眼前的问题。 In short, I want to: 简而言之,我想:

  • Do something to every item in the list. 对列表中的每个项目执行某些操作。
  • Do something else to all but the last item in the list (in effect, do something "inbetween" the items in the list). 除了列表中的最后一项之外的所有其他内容(实际上,在列表中的项目之间执行某些操作)。

For example, let's say I have a class called Equation : 例如,假设我有一个名为Equation的类:

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

I want to iterate over a list of Equation s and return a string that formats these items together; 我想迭代一个Equation列表并返回一个格式化这些项目的字符串; 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 is going to include and at the end of the returned string. 与上面的代码的问题是,它要包括and在返回的字符串的结尾。 I know that I could hack some code together, replace the foreach with a for loop, and add the inbetween element only if it's not the last item; 我知道我可以一起破解一些代码,用for循环替换foreach ,只有当它不是最后一项时才添加inbetween元素; but this seems like a hack. 但这似乎是一个黑客。

Is there a standard methodology for how to deal with this type of problem? 有没有一种标准的方法来处理这类问题?

You basically have a few different strategies for dealing with this kind problem: 你基本上有一些不同的策略来处理这种问题:

  1. Process the first (or last) item outside of the loop. 处理循环外的第一个(或最后一个)项。
  2. Perform the work and then "undo" the extraneous step. 执行工作,然后“撤消”无关的步骤。
  3. Detect that your're processing the first or last item inside the loop. 检测您正在处理循环内的第一个或最后一个项目。
  4. Use a higher-level abstraction that allows you to avoid the situation. 使用更高级别的抽象,可以避免这种情况。

Any of these options can be a legitimate way to implement a "between the items" style of algorithm. 任何这些选项都可以是实现“项目之间”算法风格的合法方式。 Which one you choose depends on things like: 您选择哪一个取决于以下内容:

  • which style you like 你喜欢哪种风格
  • how expensive "undoing work" is “撤消工作”是多么昂贵
  • how expensive each "join" step is 每个“加入”步骤的成本是多少
  • whether there are any side effects 是否有任何副作用

Amongst other things. 除此之外。 For the specific case of string, I personally prefer using string.Join() , as I find it illustrates the intent most clearly. 对于字符串的特定情况,我个人更喜欢使用string.Join() ,因为我发现它最清楚地说明了意图。 Also, in the case of strings, if you aren't using string.Join() , you should try to use StringBuilder to avoid creating too many temporary strings (a consequence of strings being immutable in .Net). 此外,在字符串的情况下,如果您不使用string.Join() ,您应该尝试使用StringBuilder来避免创建太多临时字符串(字符串在.Net中不可变的结果)。

Using string concatentation as the example, the different options break down into examples as follows. 使用字符串连接作为示例,不同的选项分解为示例,如下所示。 (For simplicity, assume Equation has ToString() as: "(" + LeftSide + Operator + RightSide + ")" (为简单起见,假设Equation有ToString()为: "(" + 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: 第二个选项如下:

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 would look something like: 第三个看起来像:

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 multiple forms in .NET, using either String.Join or Linq: 最后一个选项可以在.NET中使用String.Join或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 avoid using Aggregate() for string concatenation because it results in many intermediate, discarded strings. 就个人而言,我避免使用Aggregate()进行字符串连接,因为它会产生许多中间的,丢弃的字符串。 It's also not the most obvious way to "join" a bunch of results together - it's primarily geared for computing a "scalar" results from a collection in some arbitrary, caller-defined fashion. 它也不是将一堆结果“加入”在一起的最明显的方式 - 它主要用于以某种随意,调用者定义的方式计算集合的“标量”结果。

您可以使用String.Join()

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

You can do this with LINQ's Aggregate operator: 您可以使用LINQ的Aggregate运算符执行此操作:

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

Using a for loop with counter is perfectly reasonable if you don't want a foreach loop. 如果你不想要一个foreach循环,使用带有计数器的for循环是完全合理的。 This is why there is more than one type of looping statement. 这就是为什么有多种类型的循环语句。

If you want to process items pairwise, loop at LINQ's Aggregate operator. 如果要成对处理项目,请在LINQ的Aggregate运算符处循环。

I usualy add it before the condition, and check if its the 1st item. 我通常在条件之前添加它,并检查它是否是第1项。

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 I would look at the string.Join() function as well, +1 for Linqiness on that. 我不得不说我会看看string.Join()函数,对于Linqiness来说就是+1。 My example is a more of a traditional solution. 我的例子是一个传统的解决方案。

I generally try to prefix separators based on a condition rather than add 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 0和1和2和3和4和5和6和7和8和9

I like the String.Join method already posted. 我喜欢已发布的String.Join方法。

But when you're not using an Array this has normally been 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's something to append to
        if (output != string.Empty)
            output += " and ";

        output += "(" + e.LeftSide + e.Operator + e.RightSide + ")";
    }
    return output;
}

Of course, it's usually faster to use a StringBuilder: 当然,使用StringBuilder通常会更快:

public string FormatEquationList(List<Equation> listEquations)
{
    StringBuilder output = new StringBuilder();
    foreach (Equation e in listEquations)
    {
        // only append " and " when there's 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM