使用linq来匹配集合中的成对行
本文关键字:集合 linq 使用 | 更新日期: 2024-10-20 11:28:33
在系统中,我使用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" ""
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' ''
我不确定这是否是实现您需求的最佳方式,但这就是我所拥有的:-
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
};
});
工作Fiddle。
获取目标对象的两个部分可以迭代完成:
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
。
你所需要做的就是使用那些找到的对象来创建你的新对象。
虽然我非常喜欢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());
我想还有更优雅的方法可以做到这一点,但这会产生您期望的输出:
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 ?? "-");
}