Linq Distinct无法正确返回结果

3

我正在尝试使用 LinqDataTable 中选择不同的值。该 DataTable 是从一个包含动态列的 Excel 表格中填充的,除此之外,每个 Excel 表格都有一个名为 SERIAL NUMBER 的列名是必需的。

我有一个演示用的 DataTable,其中包含 4 个序列号:

  • 12345
  • 12345
  • 98765
  • 98765

当我执行以下操作时:

var distinctList = dt.AsEnumerable().Select(a => a).Distinct().ToList();

如果我执行

var distinctList = dt.AsEnumerable().Select(a => a.Field<string>("SERIAL NUMBER").Distinct().ToList();

我得到了正确的结果,但它只包含来自dt的一个列而不是所有其他列

我得到了所有四条记录而不是两条。请问有人能告诉我哪里错了吗。


你想在数据表中按所有列进行去重吗? - Magnus
@Magnus 我想仅通过“序列号”列进行区分,如果有更多的话,只返回其中一个,并将另一个放在重复列表中,以便最终可以警告用户发现的所有重复项。 - Izzy
5个回答

3
问题在于Distinct方法默认使用默认的相等比较器,而对于DataRow来说是通过引用进行比较的。要获得所需的结果,您可以使用允许您传递IEqualityComparer<T>Distinct重载,并传递DataRowComparer.Default

DataRowComparer<TRow>类用于比较DataRow对象的值而不是比较对象引用。

var distinctList = dt.AsEnumerable().Distinct(DataRowComparer.Default).ToList();

更多信息请参见比较数据行 (LINQ to DataSet)


谢谢您,我会尝试并告诉您我的进展。 - Izzy

2
在 ToTable 方法中,第一个参数指定是否需要 Distinct 记录,第二个参数指定我们将使用哪个列名进行去重。
DataTable returnVals = dt.DefaultView.ToTable(true, "ColumnNameOnWhichYouWantDistinctRecords");

在这个任务中,没有必要使用linq!

谢谢您的回答,但这只会返回“序列号”列。 - Izzy

2

那么,您想按序列号对它们进行分组并检索完整的DataRow吗?假设在对它们进行分组后,我们想要检索第一项:

var distinctList = dt.AsEnumerable().GroupBy(a => a.Field<string>("SERIAL NUMBER"))
                       .Select(a => a.FirstOrDefault()).Distinct().ToList();

编辑:根据要求

var distinctValues = dt.AsEnumerable().Select(a => a.Field<string>("SERIAL NUMBER")).Distinct().ToList();
var duplicateValues = dt.AsEnumerable().GroupBy(a => a.Field<string>("SERIAL NUMBER")).SelectMany(a => a.Skip(1)).Distinct().ToList();
var duplicatesRemoved = dt.AsEnumerable().Except(duplicateValues);

抱歉,我想我的问题可能不太清楚。我只想获取唯一的值并删除重复项/将它们添加到另一个列表中(如果可能的话)。我不想对它们进行分组。 - Izzy
谢谢你,你在这里使用Skip(1)的原因是什么? - Izzy
因此,通过按SerialNumber对它们进行分组,我们将获得几个项目集合,每个集合共享相同的SN。使用 .SelectMany() 我们可以将每个集合中的所有项目检索到一个平面列表中,但是我们想要 跳过避免 第一个项目,以便我们不包括该SN的“原始”或“第一个”条目,我们只想要所有重复项。清楚吗? - Innat3

1
使用Linq的GroupBy可能更加适合,听起来是这样的。
var groups = dt.AsEnumerable().GroupBy(a => a.SerialNumber).Select(_ => new {Key = _.Key, Items = _});

这将根据序列号进行分组。每个项目组具有相同的序列号,但其他属性值不同。

但这样做不会给我一个不同的列表,而是将两个项目分组到一个序列下,这不是我想要的。 - Izzy

0

试试这个:

List<string> distinctValues = (from row in dt.AsEnumerable() select row.Field<string>("SERIAL NUMBER")).Distinct().ToList();

但是对我来说,这也可以工作:

List<string> distinctValues = dt.AsEnumerable().Select(row => row.Field<string>("SERIAL NUMBER")).Distinct().ToList();

但这只会返回一个带有“序列号”列的列表,我也想要所有其他列。你的查询与我已经在问题中提出的完全相同。 - Izzy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接