Tricky LINQ查询文本文件格式转换

时间:2012-09-11 10:25:30

标签: c# linq

我在文本文件中有一个购物清单,如下所示:

BuyerId    Item;
1;         Item1;
1;         Item2;
1;         ItemN;
2;         Item1;
2;         ItemN;
3;         ItemN;

我需要将此列表转换为如下格式:

Item1; Item2; Item3; ...; ItemN    <--- For buyer 1
Item1; ...; ItemN                  <--- For buyer 2
Item1; ...; ItemN                  <--- For buyer 3

首先我解析CSV文件,如下所示:

IList<string[]> parsedcsv = (from line in lines.Skip(1) 
                             let parsedLine = line.TrimEnd(';').Split(';')
                             select parsedLine).ToList();

然后我使用LINQ对项目进行分组并将它们聚合为最终格式:

IEnumerable<string> buyers = from entry in parsedcsv
                             group entry by entry[0] into cart
                             select cart.SelectMany(c => c.Skip(1))
                                  .Aggregate((item1, item2) => 
                                      item1 + ";" + item2).Trim();

然而,正如它发生的那样,BuyerId并不是唯一的,而是经过多次重复(例如,它可以像这样重复:1,2,3,4,5,1,2,3,4, 5,1,2,3或类似1,2,3,1,2,3,1,2)。

没什么大不了的,我可以通过将项目分组到一个循环中来检查我一次只处理一个买方来轻松解决这个问题:

int lastBatchId = 0;
string currentId = parsedcsv[0][0];
for (int i = 0; i < parsedcsv.Count; i++)
{
    bool last = parsedcsv.Count - 1 == i;
    if (parsedcsv[i][0] != currentId || last)
    {
         IEnumerable<string> buyers = from entry in parsedcsv.Skip(lastBatchId)
              .Take(i - lastBatchId + (last ? 1 : 0))
         ...
         lastBatchId = i;
         currentId = parsedcsv[i][0];
         ...

...但是,这不是最优雅的解决方案。我几乎可以肯定这只能用LINQ来完成。

有人可以帮帮我吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

您应该查看GroupAdjacent

答案 1 :(得分:0)

我不确定这是最好的解决方案,但你说你想要一个纯粹的Linq答案,所以在这里你有:

var result = from r in (

    from l in lines.Skip(1)
    let data = l.Split(new string[]{";"," "}, 
                       StringSplitOptions.RemoveEmptyEntries)
    select new { Id = data.First(), Item = data.Skip(1).First() })
    .Aggregate(new 
                { 
                    Rows = Enumerable.Repeat(new 
                                            { 
                                                Id = string.Empty, 
                                                Items = new List<string>() 
                                            }, 1).ToList(), 
                    LastID = new List<string>() { "" } 
                }, 
                (acc, x) => 
                { 
                    if (acc.Rows[0].Id == string.Empty)
                        acc.Rows.Clear();
                    if (acc.LastID[0] != x.Id)
                        acc.Rows.Add(new 
                                    {
                                        Id = x.Id, 
                                        Items = new List<string>() 
                                    });
                    acc.Rows.Last().Items.Add(x.Item);
                    acc.LastID[0] = x.Id;
                    return acc; 
                }       
    ).Rows
select new 
{ 
    r.Id, 
    Items = string.Join(";", from x in r.Items 
                             select x) 
};

我写得非常快,可以改进,我不喜欢它特别是因为它采用了几个技巧,但它是纯粹的Linq并且可能是一个起点。