确定数据表中的重复项

时间:2012-05-22 17:28:46

标签: c# datatable duplicates

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

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

编辑:我想我应该注意,有重复的行应该删除 ALL ,而不仅仅是该行的重复项。例如,如果有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;
    }
}

2 个答案:

答案 0 :(得分: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();

答案 1 :(得分: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值将由C#中的DBNull类型表示。在这种情况下,使用as运算符只会为您提供null(而不是InvalidCastException。 如果您确定,那么您正在处理INT NOT NULL,使用(int)进行投射。

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

EDIT1:

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

EDIT2:

添加了string示例,一些评论

如需更直接的方式,请查看How to select distinct rows in a datatable and store into an array

EDIT3:

有关演员的一些解释。

我建议的其他链接与您的代码相同。我忘记了你原来的意图;-)我刚看到你的代码并回答了最明显的错误,我看到了 - 抱歉

以下是我将如何解决问题

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。你需要将演员阵容调整到合适的类型。请参阅上面的添加内容。