LINQ条件聚合基于下一个元素的值

时间:2012-03-29 23:38:32

标签: c# linq linq-to-objects

这个pesudo代码的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解决方案来解决这个问题。有吗?

4 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

可以做这样的事情:

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

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

但是,正如您所看到的,这不是很易读。我意识到你正在寻找一个学习练习,但我会在任何一天都有一个很好的可读foreach循环。

答案 2 :(得分: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));
        }
    }
}

答案 3 :(得分:0)

尝试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()))