2012-03-29 103 views
4

这个pesudo-code的LINQ等价物是什么:“给定一个字符串列表,对于每个不包含制表符的字符串,将它连接起来(用管道分隔符)到前一个字符串的末尾,并返回结果序列“?基于下一个元素值的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的解决这个问题。在那儿?

+3

说实话,这不是我会建议使用LINQ的,尽管我确信有办法这样做。这与它的设计不符。 – Servy 2012-03-29 23:53:42

回答

3

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

正确枚举序列意味着没有项目知道其他项目,这显然不适用于您的案例。使用for循环,以便您可以按顺序逐个清理字符串。

+0

thx为您的答案 - 有没有一些超载的聚合体做什么OP后? – BKSpurgeon 2017-02-03 04:14:18

0

可以做这样的事情:

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

我被骗了,并得到ReSharper的生成这对我来说。这很接近 - 它在顶部留下了一个空行。

但是,正如你所看到的,这是不太可读的。我意识到你正在寻找一个学习练习,但我会采取一个很好的可读foreach循环在这一天。

0

只是为了我的好奇心。

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

resultList应该包含你想要的。

请注意,这不是一个最佳解决方案。行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)); 
     } 
    } 
} 
0

尝试一个for()解决方案之后,我尝试了LINQ溶液,用下面的一个上来。对于我相当小的(10K行)文件,速度足够快,我不关心效率,并且我发现它比等效的解决方案更具可读性。

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())) 
相关问题