如何使用 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 列有什么类型?你一直在做ToStringTrim。为什么数据在放入表格之前没有清除多余的空格? 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# 将一个数据表拆分为两个(匹配记录)(不匹配记录)的主要内容,如果未能解决你的问题,请参考以下文章

如何在C#中将带逗号的字符串拆分为两个字符串[重复]

将整数拆分并存储为两个字节

如何使用 REGEX 将作者拆分为对象或数组 C#?

在特定字符处拆分字符串但忽略某些情况 C# LINQ

如何使用 SAS 将字符串拆分为两个变量

C#将LINQ数据集转换为Datatable