1

我有一个从 CSV 文件加载的数据表。我需要根据数据表中的两列(product_idowner_org_id)确定哪些行是重复的。一旦我确定了这一点,我就可以使用该信息来构建我的结果,这是一个仅包含非唯一行的数据表,以及一个仅包含唯一行的数据表。

我在这里查看了其他示例,到目前为止我提出的代码确实可以编译和执行,但似乎认为数据中的每一行都是唯一的。实际上,在测试数据中有 13 行,只有 6 行是唯一的。很明显我做错了什么。

编辑:我想我应该注意,应该全部删除具有重复项的行,而不仅仅是该行的重复项。例如,如果有 4 个重复项,则应删除所有 4 个而不是 3 个,从 4 个中保留一个唯一行。

EDIT2:或者,如果我可以选择所有重复的行(而不是尝试选择唯一的行),那对我来说很好。无论哪种方式都可以让我得到最终结果。

处理方法中的代码:

MyRowComparer myrc = new MyRowComparer();
var uniquerows = dtCSV.AsEnumerable().Distinct(myrc);

以及以下内容:

public class MyRowComparer : IEqualityComparer<DataRow>
{
    public bool Equals(DataRow x, DataRow y)
    {
        //return ((string.Compare(x.Field<string>("PRODUCT_ID"),   y.Field<string>("PRODUCT_ID"),   true)) ==
        //        (string.Compare(x.Field<string>("OWNER_ORG_ID"), y.Field<string>("OWNER_ORG_ID"), true)));
        return
            x.ItemArray.Except(new object[] { x[x.Table.Columns["PRODUCT_ID"].ColumnName] }) ==
            y.ItemArray.Except(new object[] { y[y.Table.Columns["PRODUCT_ID"].ColumnName] }) &&
            x.ItemArray.Except(new object[] { x[x.Table.Columns["OWNER_ORG_ID"].ColumnName] }) ==
            y.ItemArray.Except(new object[] { y[y.Table.Columns["OWNER_ORG_ID"].ColumnName] });
    }

    public int GetHashCode(DataRow obj)
    {
        int y = int.Parse(obj.Field<string>("PRODUCT_ID"));
        int z = int.Parse(obj.Field<string>("OWNER_ORG_ID"));
        int c = y ^ z;
        return c;
    }
}
4

2 回答 2

3

您可以使用 LINQ-To-DataSet 和Enumerable.Except/ Intersect

var tbl1ID = tbl1.AsEnumerable()
        .Select(r => new
        {
            product_id = r.Field<String>("product_id"),
            owner_org_id = r.Field<String>("owner_org_id"),
        });
var tbl2ID = tbl2.AsEnumerable()
        .Select(r => new
        {
            product_id = r.Field<String>("product_id"),
            owner_org_id = r.Field<String>("owner_org_id"),
        });


var unique = tbl1ID.Except(tbl2ID);
var both = tbl1ID.Intersect(tbl2ID);

var tblUnique = (from uniqueRow in unique
                join row in tbl1.AsEnumerable()
                on uniqueRow equals new
                {
                    product_id = row.Field<String>("product_id"),
                    owner_org_id = row.Field<String>("owner_org_id")
                }
                select row).CopyToDataTable();
var tblBoth = (from bothRow in both
              join row in tbl1.AsEnumerable()
              on bothRow equals new
              {
                  product_id = row.Field<String>("product_id"),
                  owner_org_id = row.Field<String>("owner_org_id")
              }
              select row).CopyToDataTable();

编辑:显然我误解了你的要求。因此,您只有一个DataTable并且想要获得所有唯一且所有重复的行,这更加直接。您可以使用Enumerable.GroupBy包含两个字段的匿名类型:

var groups = tbl1.AsEnumerable()
    .GroupBy(r => new
    {
        product_id = r.Field<String>("product_id"),
        owner_org_id = r.Field<String>("owner_org_id")
    });
var tblUniques = groups
    .Where(grp => grp.Count() == 1)
    .Select(grp => grp.Single())
    .CopyToDataTable();
var tblDuplicates = groups
    .Where(grp => grp.Count() > 1)
    .SelectMany(grp => grp)
    .CopyToDataTable();
于 2012-05-22T17:46:55.807 回答
1

您的标准已关闭。您正在比较您不感兴趣(Except排除)的对象集。

相反,尽可能清晰(数据类型)并保持简单:

public bool Equals(DataRow x, DataRow y)
{   
    // Usually you are dealing with INT keys
    return (x["PRODUCT_ID"] as int?) == (y["PRODUCT_ID"] as int?)
      && (x["OWNER_ORG_ID"] as int?) == (y["OWNER_ORG_ID"] as int?);

    // If you really are dealing with strings, this is the equivalent:
    // return (x["PRODUCT_ID"] as string) == (y["PRODUCT_ID"] as string)
    //  && (x["OWNER_ORG_ID"] as string) == (y["OWNER_ORG_ID"] as string)
}  

检查null是否有可能。也许您想排除相等的行,因为它们的 ID 为空。

观察int?. 这不是一个错字。如果您要处理来自列的数据库值,则问号是必需的NULL。原因是NULL值将由DBNullC# 中的类型表示。在这种情况下,使用as运算符只会为您提供null(而不是InvalidCastException. 如果您确定,您正在处理INT NOT NULL,请使用(int).

字符串也是如此。(string)断言您期望非空 DB 值。

编辑1:

类型错了。ItemArray 不是哈希表。直接使用该行。

编辑2:

添加string示例,一些注释

有关更直接的方法,请检查如何在数据表中选择不同的行并存储到数组中

编辑3:

关于演员阵容的一些解释。

我建议的另一个链接与您的代码相同。我忘记了您的初衷;-) 我刚刚看到您的代码并响应了最明显的错误,我看到了-抱歉

这是我解决问题的方法

using System.Linq;
using System.Data.Linq;

var q = dtCSV
    .AsEnumerable()
    .GroupBy(r => new { ProductId = (int)r["PRODUCT_ID"], OwnerOrgId = (int)r["OWNER_ORG_ID"] })
    .Where(g => g.Count() > 1).SelectMany(g => g);

var duplicateRows = q.ToList();

我不知道这是否 100% 正确,我手头没有 IDE。而且您需要将演员表调整为适当的类型。见我上面的补充。

于 2012-05-22T17:36:49.230 回答