【问题标题】:How do I iterate "between" items in an array / collection / list?如何迭代数组/集合/列表中的“之间”项目?
【发布时间】:2010-02-09 18:14:44
【问题描述】:

这个问题多年来一直困扰着我,当有更好的解决方案时,我总是觉得自己想出了一个技巧。当您想要对列表中的所有项目执行某项操作,然后在这些项目之间添加一些内容时,就会出现手头的问题。简而言之,我想:

  • 对列表中的每个项执行操作。
  • 对列表中除了最后一个项目之外的所有项目执行其他操作(实际上,在列表中的项目“中间”执行操作)。

例如,假设我有一个名为 Equation 的类:

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

我想遍历Equations 的列表并返回一个将这些项目格式化在一起的字符串;类似于以下内容:

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;
}

上面代码的问题是它会在返回字符串的末尾包含and。我知道我可以一起破解一些代码,将foreach 替换为for 循环,并仅在它不是最后一项时添加inbetween 元素;但这似乎是一个黑客行为。

是否有处理此类问题的标准方法?

【问题讨论】:

    标签: c# .net string iteration


    【解决方案1】:

    您基本上有几种不同的策略来处理此类问题:

    1. 处理循环外的第一个(或最后一个)项目。
    2. 执行工作,然后“撤消”无关步骤。
    3. 检测到您正在处理循环内的第一项或最后一项。
    4. 使用更高级别的抽象来避免这种情况。

    这些选项中的任何一个都可以是实现“项目之间”算法风格的合法方式。您选择哪一个取决于以下内容:

    • 你喜欢哪种风格
    • “撤销工作”的代价有多大
    • 每个“加入”步骤的成本是多少
    • 是否有副作用

    除其他外。对于字符串的具体情况,我个人更喜欢使用string.Join(),因为我发现它最清楚地说明了意图。此外,对于字符串,如果您不使用string.Join(),则应尝试使用StringBuilder 以避免创建过多的临时字符串(字符串在.Net 中不可变的结果)。

    以字符串连接为例,不同的选项分解为如下示例。 (为简单起见,假设方程有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();
    }
    

    第二个选项看起来像:

    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 );
    }
    

    第三个看起来像:

    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 );
        }
    }
    

    最后一个选项可以在 .NET 中采用多种形式,使用 String.Join 或 Linq:

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

    或:

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

    就我个人而言,我避免使用Aggregate() 进行字符串连接,因为它会导致许多中间字符串被丢弃。这也不是将一堆结果“连接”在一起的最明显方式 - 它主要用于以某种任意、调用者定义的方式计算集合中的“标量”结果。

    【讨论】:

      【解决方案2】:

      您可以使用String.Join()

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

      【讨论】:

      • 不错的解决方案,测试了您的代码,需要稍作修改 :-) string.Join(" 和 ",lists.Select(e=>String.Format("({0}{1}{ 2})",e.LeftSide,e.Operator,e.RightSide)).ToArray());
      【解决方案3】:

      您可以使用 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 + ")");
      }
      

      【讨论】:

        【解决方案4】:

        如果您不想要foreach 循环,则使用带有计数器的for 循环是完全合理的。这就是为什么有不止一种循环语句的原因。

        如果要成对处理项目,请在 LINQ 的 Aggregate 运算符处循环。

        【讨论】:

          【解决方案5】:

          我通常在条件之前添加它,并检查它是否是第一项。

          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; 
          }
          

          我不得不说我也会看看 string.Join() 函数,+1 表示 Linqiness。我的例子是一个更传统的解决方案。

          【讨论】:

            【解决方案6】:

            我通常会尝试根据条件为分隔符添加前缀,而不是将它们添加到末尾。

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

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

            【讨论】:

              【解决方案7】:

              我喜欢已经发布的 String.Join 方法。

              但是当你不使用数组时,这通常是我解决这个问题的方法:

              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;
              }
              

              当然,使用 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();
              }
              

              【讨论】:

                猜你喜欢
                • 2016-08-06
                • 2017-04-09
                • 2013-07-13
                • 1970-01-01
                • 1970-01-01
                • 2019-06-10
                • 1970-01-01
                • 2011-01-25
                • 2016-04-03
                相关资源
                最近更新 更多