我有一个从CSV文件加载的数据表。我需要根据数据表中的两列(product_id
和owner_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;
}
}
答案 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。你需要将演员阵容调整到合适的类型。请参阅上面的添加内容。