如何使用 LINQ C# 将一个数据表拆分为两个(匹配记录)(不匹配记录)
Posted
技术标签:
【中文标题】如何使用 LINQ C# 将一个数据表拆分为两个(匹配记录)(不匹配记录)【英文标题】:How to split One Datatable into Two (matched record) (unmatched record) using LINQ C# 【发布时间】:2020-11-28 02:11:43 【问题描述】:我有一个数据表,我需要将记录分为两个,数据表(匹配)和数据表(不匹配)。对于要匹配的记录,必须有两行具有相同的 RRN,并且这两个 AMOUNT 之和为零。
我使用下面的代码实现了这一点。
if (InputCollection.Rows.Count > 0)
for (int i = InputCollection.Rows.Count - 1; i-- > 0;)
var selectedRecord = InputCollection.Rows[i];
if (selectedRecord["RRN"].ToString() != "")
for (int j = 0; j < InputCollection.Rows.Count - 1; j++)
if (i != j)
var thisRecord = InputCollection.Rows[j];
var selectedRRN = selectedRecord["RRN"].ToString();
var thisRRN = thisRecord["RRN"].ToString();
if (selectedRRN.Trim() == thisRRN.Trim())
if (Decimal.Parse(selectedRecord["AMOUNT"].ToString()) + Decimal.Parse(thisRecord["AMOUNT"].ToString()) == 0)
MatchedOutput.ImportRow(InputCollection.Rows[i]);
MatchedOutput.ImportRow(InputCollection.Rows[j]);
InputCollection.Rows.Remove(InputCollection.Rows[j]);
InputCollection.Rows.Remove(InputCollection.Rows[i]);
break;
代码的问题是它太慢了。如何在 c# 中使用 LINQ 查询重写上面的代码?谢谢。
【问题讨论】:
PRN
列有什么类型?你一直在做ToString
和Trim
。为什么数据在放入表格之前没有清除多余的空格?
从Decimal.Parse
判断,Amount
列存储数字。但是,然后将此列设为typeof(decimal)
。然后你就不需要解析了,代码会运行得更快。
Input Collection.Rows.Remove
减慢了很多,因为它会在删除一列后移动所有其他列。摆脱对这个方法的调用。
@AlexanderPetrov '为什么数据在放入表格之前没有清除多余的空格? ' 因为它来自一个 excel 文件
【参考方案1】:
这并不简单,Linq 也不是完成的正确方法。见下面的代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
class Program
static void Main(string[] args)
DataTable dt = new DataTable();
dt.Columns.Add("RRN", typeof(string));
dt.Columns.Add("Amount", typeof(decimal));
dt.Rows.Add(new object[] 1, 1.0 );
dt.Rows.Add(new object[] 1, -1.0 );
dt.Rows.Add(new object[] 1, 2.0 );
dt.Rows.Add(new object[] 1, -2.0 );
dt.Rows.Add(new object[] 1, 3.0 );
dt.Rows.Add(new object[] 2, -3.0 );
dt.Rows.Add(new object[] 2, 1.0 );
dt.Rows.Add(new object[] 2, -1.0 );
dt.Rows.Add(new object[] 3, 1.0 );
dt.Rows.Add(new object[] 3, -1.0 );
dt.Rows.Add(new object[] 4, 1.0 );
var groupRRN = dt.AsEnumerable().Select(x => new Match() row = x, RRN = x.Field<string>("RRN"), amount = x.Field<decimal>("Amount"), matched = false ).GroupBy(x => x.RRN).ToList();
DataTable matchedTable = dt.Clone();
DataTable unmatchedTable = dt.Clone();
foreach (var group in groupRRN)
for (int i = group.Count() - 1; i >= 1; i--)
decimal amountI = group.Skip(i).First().amount;
for (int j = i - 1; j >= 0; j--)
Match matchJ = group.Skip(j).First();
if (!matchJ.matched)
decimal amountJ = matchJ.amount;
if (amountI + amountJ == 0)
group.Skip(i).First().matched = true;
group.Skip(j).First().matched = true;
break;
foreach (Match match in group)
if (match.matched == true)
matchedTable.Rows.Add(match.row.ItemArray);
else
unmatchedTable.Rows.Add(match.row.ItemArray);
public class Match
public DataRow row get; set;
public Boolean matched get; set;
public string RRN get; set;
public decimal amount get; set;
【讨论】:
谢谢。我觉得这会比我目前的慢。一点洞察数据的本质。这个数据表有动态列,但都有 RRN 和 AMOUNT。最大列数可达 110,记录数可达 100 万。 对于非常大的表,我的速度更快。你的算法是 N^2/2。我的我创建了一个哈希表(分组 RRN),它是 log(N) 来构建表。然后假设每个组的大小相同,则在每个组中搜索对会快得多。所以我的是 log(N) + (Log(N)^2/2) + Log(N) = 2Log(N) + Log(N)^2/2。所以 Log(N)^2 小于 N^2。以上是关于如何使用 LINQ C# 将一个数据表拆分为两个(匹配记录)(不匹配记录)的主要内容,如果未能解决你的问题,请参考以下文章