使用linq匹配集合中的行对

时间:2015-03-05 10:07:42

标签: c# linq

在系统中,我使用对RowMod标志的新旧行对接收数据的修改,例如删除,添加,更新和未更改的行通过:

RowID Data    RowMod
Row1  "fish"  "" 
Row1  "fish"  "D" 
Row2  "cat"   "A"
Row3  "fox"   ""
Row3  "dog"   "U"
Row4  "mouse" ""

我想使用每行所拥有的RowID来匹配这些内容并得到类似的内容:

RowID OldData NewData RowMod
Row1  "fish"  null    "D"
Row2  null    "cat"   "A"
Row3  "fox"   "dog"   "U"
Row4  "mouse" "mouse" ""

5 个答案:

答案 0 :(得分:2)

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<DataRow> rows = new[]
        {
            new DataRow(1,"fish",""),
            new DataRow(1,"fish","D"),
            new DataRow(2,"cat","A"),
            new DataRow(3,"fox",""),
            new DataRow(3,"dog","U"),
            new DataRow(4,"mouse","")
        };

        var result = rows
            .GroupBy(x => x.Id)
            .Select(g => new 
            {
                Count = g.Count(),
                Id = g.First().Id,
                FirstRow = g.First(),
                LastRow = g.Last() 
            }).Select(item => new 
            {
                RowId = item.Id,
                OldData = item.Count == 1 && item.FirstRow.RowMod != "" ? null : item.FirstRow.Data,
                NewData = item.LastRow.RowMod == "D" ? null : item.LastRow.Data,
                RowMod = item.LastRow.RowMod 
            });

            //Or using query syntax
            var result2 = from x in rows
                          orderby x.Id, x.RowMod
                          group x by x.Id into g
                          select new
                          {
                              RowId = g.First().Id,
                              OldData = g.Count() == 1 && g.First().RowMod != "" ? null : g.First().Data,
                              NewData = g.Last().RowMod == "D" ? null : g.Last().Data,
                              RowMod = g.Last().RowMod
                          };

        // Test
        Console.WriteLine("RowID\tOldData\tNewData\tRowMod");
        foreach (var item in result)
        {
            Console.WriteLine("{0}\t'{1}'\t'{2}'\t'{3}'",item.RowId,item.OldData ?? "null",item.NewData ?? "null",item.RowMod);
        }
    }
}

public class DataRow
{
    public int Id { get; set; }
    public string Data { get; set; }
    public string RowMod { get; set; }

    public DataRow(int id, string data, string rowMod)
    {
        Id = id;
        Data = data;
        RowMod = rowMod;
    }
}

输出:

RowID   OldData NewData RowMod
1       'fish'  'null'  'D'
2       'null'  'cat'   'A'
3       'fox'   'dog'   'U'
4       'mouse'  'mouse' ''

答案 1 :(得分:1)

我不确定这是否是达到您要求的最佳方式,但这就是我所拥有的: -

var result = rows.GroupBy(x => x.RowId)
                 .Select(x => 
          {
             var firstData = x.FirstOrDefault();
             var secondData = x.Count() == 1 ? x.First().RowMod == "A" ? firstData : null
                                             : x.Skip(1).FirstOrDefault();
          return new
          {
              RowId = x.Key,
              OldData = firstData.RowMod == "A" ? null : firstData.Data,
              NewData = secondData != null ? secondData.Data : null,
              RowMod = String.IsNullOrEmpty(firstData.RowMod) && secondData != null ?
                                      secondData.RowMod : firstData.RowMod
          };
      });

Working Fiddle

答案 2 :(得分:0)

获取目标对象的两个部分可以迭代完成:

foreach(var rowId in myList.Select(x => x.RowId).Distinct())
{
    //get the left item
    var leftItem = myList.SingleOrDefault(x => x.RowId == rowId && String.IsNullOrWhiteSpace(x.rowmod);

    //get the right item
    var rightItem = myList.SingleOrDefault(x => x.RowId == rowId && !String.IsNullOrWhiteSpace(x.rowmod);
}

您的问题并未指定如何创建第二个对象。这是一个不同的类吗? 无论哪种方式,您都可以从上面的代码段中推断出,如果原始集合中不存在任何一个项目null

您需要做的就是使用那些找到的对象来创建新对象。

答案 3 :(得分:0)

我想有更优雅的方法可以做到这一点,但这会产生你期望的输出:

public class MyClass
            {
                public int RowID { get; set; }
                public string Data { get; set; }
                public string RowMod { get; set; }
            }

  var result = (from id in myList.Select(x => x.RowID).Distinct()
            let oldData = myList.Where(x => x.RowID == id).SingleOrDefault(x => x.RowMod.Equals("")) != null
                ? myList.Where(x => x.RowID == id).Single(x => x.RowMod.Equals("")).Data
                : null
            let newData = myList.Where(x => x.RowID == id).SingleOrDefault(x => !x.RowMod.Equals("")) != null
                ? myList.Where(x => x.RowID == id).Single(x => !x.RowMod.Equals("")).Data
                : null
            let rowMod = myList.Where(x => x.RowID == id).SingleOrDefault(x => !x.RowMod.Equals("")) != null
                ? myList.Where(x => x.RowID == id).Single(x => !x.RowMod.Equals("")).RowMod
                : null
            select new
                   {
                       RowID = id,
                       OldData = oldData,
                       NewData = rowMod == null ? oldData : rowMod.Equals("D") ? null : newData,
                       RowMod = rowMod
                   });
foreach (var item in result)
            {
                Console.WriteLine("{0} {1} {2} {3}", item.RowID, item.OldData ?? "null", item.NewData ?? "null", item.RowMod ?? "-");
            }

答案 4 :(得分:0)

虽然我非常喜欢LINQ,但我不认为这是合适的,因为你想在迭代时缓冲一些值。如果你使用LINQ执行此操作,它最好不会表现良好,最坏的情况是它会多次迭代集合。在我看来,它看起来也更加清洁。

IEnumerable<TargetClass> MapOldValues(IEnumerable<SourceClass> source)
{
    var buffer = new Dictionary<string, string>();
    foreach(var item in source)
    {
        string oldValue;
        buffer.TryGetValue(item.RowId, out oldValue); 
        yield return new TargetClass
                          {
                              RowId = item.RowId, 
                              OldData = oldValue, 
                              NewData = (item.RowMod == "D" ? null : item.Data), 
                              RowMod = item.RowMod  };
        // if the rows come sorted by ID, you can clear old values from
        // the buffer to save memory at this point:
        // if(oldValue == null) { buffer.Clear(); }
        buffer[item.RowId] = item.Data;
    }
}

如果您只想要最新的更新,可以使用LINQ:

var latestChanges = MapOldValues(source).GroupBy(x => x.RowId).Select(x => x.Last());