从数据表LINQ列创建一个元素列表

4
我想知道如何将DataTable的一列元素转换为字符串类型的列表,并对元素进行分组以避免重复。
例如,我的DataTable看起来像这样DataTable,我希望使用LINQ创建一个仅包含“User”元素的列表,且不重复出现。
我尝试使用的代码是:
InvoiceList = InvoiceDT.AsEnumerable().GroupBy(Function(r) r("User").ToString).ToList(Function(g) g.ToList())

但是对于我来说并不起作用,因为我是LINQ的新手,仍然在形成结构方面存在问题。

2个回答

4
我会使用这个:
InvoiceList = InvoiceDT.AsEnumerable().Select(Function(r) r("User").ToString()).Distinct().ToList()

如果您需要 GroupBy 解决方案,可以这样做:

InvoiceList = InvoiceDT.AsEnumerable().GroupBy(Function(r) r("User").ToString()).Select(Function(g) g.Key).ToList()

你代码出问题的地方在于试图向ToList传递委托,它不接受委托(而且你也不会对g进行ToList操作,因为它是包含所有变量属性的数据行列表)。

我们将IGrouping(类似于对象列表,它们共享列表中IGrouping表示的属性键)重塑成一系列字符串键,通过选择键然后ToList 来实现。


开发人员之间存在许多关于ToList和ToArray等事项的往返 - 有些人普遍使用ToList,因为对于元素数量未知的集合,列表和数组都会以相同的方式增长和调整大小,但是使用ToArray需要一个附加的调整大小步骤来修剪任何未使用的插槽。通常,在整体性能考虑方面,这大多是微不足道的,并应该权衡释放内存与调整大小的好处。深入了解细节已经超出了本回答的范围,但您可以阅读一些巨大的博客文章来了解更多信息。

我个人认为,通过调用生成相关类型结果的方法来生成明智的代码更为重要;如果需要列表功能(添加/插入/删除),我就使用ToList。如果一个数组适合后续用途(读/写/随机访问,无插入或删除),我更喜欢ToArray。如果我只会枚举它,我根本不需要To...任何东西 - 我只需ForEach查询结果,这可能比其他任何方法都更能提高性能,因为它意味着我可能不必枚举整个集合(如果我早停)或一次性分配内存(如果我正在写入套接字或文件)。

关于ToString的使用,如果您认为您将陷入每列都执行ToString以获取字符串的模式中,则值得避免。如果该列已经是字符串,则将DataRow.Item给你的对象转换为字符串是可以接受的方法。如果该列是另一种类型,则最好将其转换为:

  • DirectCast(r("Age"), Integer)
  • r.Field(Of Integer)("Age")

问题在于,它很冗长,难看,并且智能感应不帮助您编写Age或知道它是Int。在VB中的LINQ已经够啰嗦了,不要再加油了。如果您正在使用已知结构的数据表,那么创建强类型表会更加美观:

  • 向项目中添加新的 DataSet 类型的文件
  • 打开它,使设计表面出现。在属性网格中将其命名为一些合理的名称,例如 AccountsDataSet
  • 右键单击,添加表,将其命名为 Invoices
  • 右键单击空表,添加列,将其命名为 User

然后像这样使用:

Dim dt as new AccountsDataSet.InvoicesDataTable

用以下方式填充:

dt.AddInvoicesRow("John Smith", ... other properties here)

可以这样查询:

dt.Select(Function(r) r.User).Distinct()

使用对象形式访问列名比使用字符串更方便,而且它们是需要转换的对象。

将数据集生成器视为一种快速、可视的方式来创建具有命名、类型属性的poco类。


我会建议使用 ToList,除非你正在使用需要数组的 API,因为相较于 ToListToArray 可能需要额外的分配。 - NetMage
由于您已经在使用LINQ to DataSet(即使您没有),我建议将r("User").ToString()更改为r.Field(Of String)("User") - jmcilhinney
是的。就我个人而言,我会使用直接转换* - 我将其保留为ToString,因为在这种情况下它已经是字符串列,OP显然熟悉它,并且相对于手头的任务来说它是相对辅助的,但是使用ToString可能会鼓励一个人陷入每次都使用它的陷阱,即使在非字符串上也是如此。与此相反,Field确实鼓励了一种强制转换行为,值得一提。 *实际上,我会像描述的那样制作一个强类型DS,这样我就不会将任何东西都变成字符串-更快的设置,更容易的使用,更清洁的代码,更少的错误。 - Caius Jard

0

试试这个

dim list as List(of string) = InvoiceDT.Rows.
    Cast(of DataRow)().
    Select(Function(r)  r("User").ToString()).
    Distinct().
    ToList()

在这里,你将行集合转换为 IEnumerable(of DataRow),其余部分都是琐事。


1
这基本上与已发布的答案完全相同。 - Caius Jard
@CaiusJard 请恕我直言,另一个答案使用了 InvoiceDT.AsEnumerable(),我认为这是一个多余的扩展方法对于 DataTable。例如在 fiddle 中,VB 可以通过添加 System.Data.DataSetExtensions 获得该方法,但对于 C# 我并不需要添加它。 - T.S.

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