【问题标题】:LINQ conditional aggregation based on next elements' values基于下一个元素的值的 LINQ 条件聚合
【发布时间】:2012-03-29 23:38:32
【问题描述】:

什么是这个伪代码的一个好的 LINQ 等效项:“给定一个字符串列表,对于每个不包含制表符的字符串,将它(使用管道分隔符)连接到前一个字符串的末尾,并且返回结果序列”?

更多信息:

我有一个List<string> 代表制表符分隔的文本文件中的行。每行中的最后一个字段始终是多行文本字段,并且该文件是由错误处理带有嵌入换行符的字段的错误系统生成的。所以我最终得到了一个这样的列表:

1235 \t This is Record 1
7897 \t This is Record 2
8977 \t This is Record 3
continued on the next line
and still continued more
8375 \t This is Record 4

我想通过将所有孤立行(没有制表符的行)连接到前一行的末尾来合并这个列表。像这样:

1235 \t This is Record 1
7897 \t This is Record 2
8977 \t This is Record 3|continued on the next line|and still continued more
8375 \t This is Record 4

使用for() 循环解决这个问题很容易,但我正在努力提高我的 LINQ 技能,我想知道是否有一个相当有效的 LINQ 解决方案来解决这个问题。有吗?

【问题讨论】:

  • 老实说,我不建议使用 LINQ,尽管我确信有办法做到这一点。它只是不符合它的设计目的。

标签: c# linq linq-to-objects


【解决方案1】:

这不是应该使用 LINQ 解决的问题。 LINQ 是为枚举而设计的,而这最好通过迭代来解决。

正确枚举序列意味着没有项目了解其他项目,这显然不适用于您的情况。使用for 循环,这样您就可以一一按顺序清晰地遍历字符串。

【讨论】:

  • 感谢您的回答 - 是不是有一些 Aggregate 的重载会执行 OP 之后的操作?
【解决方案2】:

只是为了我的好奇心。

var originalList = new List<string>
{
    "1235 \t This is Record 1",
    "7897 \t This is Record 2",
    "8977 \t This is Record 3",
    "continued on the next line",
    "and still continued more",
    "8375 \t This is Record 4"
};

var resultList = new List<string>();

resultList.Add(originalList.Aggregate((workingSentence, next) 
    => { 
            if (next.Contains("\t"))
            {
                resultList.Add(workingSentence);    
                return next;
            }
            else
            {
                workingSentence += "|" + next;
                return workingSentence;
            }
    }));

结果列表应该包含你想要的。

请注意,这不是最佳解决方案。 workingSentence += "|" + next; 行可能会根据您的数据模式创建大量临时对象。

最佳解决方案可能涉及保留多个索引变量以领先于字符串,并在下一个字符串包含制表符时将它们连接起来,而不是如上所示逐个连接。但是,由于边界检查和保留多个索引变量,它将比上述更复杂:)。

更新:以下解决方案不会为连接创建临时字符串对象。

var resultList = new List<string>();
var tempList = new List<string>();

tempList.Add(originalList.Aggregate((cur, next)
    => {
            tempList.Add(cur);
            if (next.Contains("\t"))
            {
                resultList.Add(string.Join("|", tempList));
                tempList.Clear();       
            }
            return next;
    }));

resultList.Add(string.Join("|", tempList));

以下是使用for循环的解决方案。

var resultList = new List<string>();
var temp = new List<string>();
for(int i = 0, j = 1; j < originalList.Count; i++, j++)
{
    temp.Add(originalList[i]);
    if (j != originalList.Count - 1)
    {   
        if (originalList[j].Contains("\t"))
        {
            resultList.Add(string.Join("|", temp));
            temp.Clear();
        }
    }
    else // when originalList[j] is the last item
    {
        if (originalList[j].Contains("\t"))
        {
            resultList.Add(string.Join("|", temp));
            resultList.Add(originalList[j]);
        }
        else
        {
            temp.Add(originalList[j]);
            resultList.Add(string.Join("|", temp));
        }
    }
}

【讨论】:

    【解决方案3】:

    可以做这样的事情:

    string result = records.Aggregate("", (current, s) => current + (s.Contains("\t") ? "\n" + s : "|" + s));
    

    我作弊并让 Resharper 为我生成这个。这很接近 - 但它在顶部留下了一个空白行。

    但是,如您所见,这不是很可读。我知道您正在寻找一个学习练习,但我会在任何一天都使用一个可读的foreach 循环。

    【讨论】:

      【解决方案4】:

      在尝试了for() 解决方案后,我尝试了 LINQ 解决方案并提出了以下解决方案。对于我相当小的(10K 行)文件,它足够快,我并不关心效率,而且我发现它比等效的for() 解决方案更具可读性。

      var lines = new List<string>      
      {      
          "1235 \t This is Record 1",      
          "7897 \t This is Record 2",      
          "8977 \t This is Record 3",      
          "continued on the next line",      
          "and still continued more",      
          "8375 \t This is Record 4"      
      };  
      var fixedLines = lines
              .Select((s, i) => new 
                  { 
                      Line = s, 
                      Orphans = lines.Skip(i + 1).TakeWhile(s2 => !s2.Contains('\t')) 
                  })
              .Where(s => s.Line.Contains('\t'))
              .Select(s => string.Join("|", (new string[] { s.Line }).Concat(s.Orphans).ToArray()))
      

      【讨论】:

        猜你喜欢
        • 2016-07-06
        • 1970-01-01
        • 2022-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-04
        • 2015-12-31
        • 2015-06-21
        相关资源
        最近更新 更多