【问题标题】:nested foreach loops returns only distinct嵌套的 foreach 循环只返回不同的
【发布时间】:2015-04-12 06:55:40
【问题描述】:

我有一个数据库,其中每个电子邮件地址都应该属于一个唯一的客户,但我有很多重复项。我使用 sql 查询列出客户 ID、电子邮件地址对,每次出现多个客户 ID 映射到单个电子邮件地址。结果看起来像这样(更改地址以保护无辜者)

Customer ID   Email
101233        bob@myaddress.com
108993        bob@myaddress.com
113224        bob@myaddress.com
89223         mary@otherdomain.com
188223        mary@otherdomain.com

在 c# 中,我将其填充到一个名为 dt 的 DataTable 中,其中包含 722 行。我用它来制作第二个名为 distinctTbl 的 DataTable,它有 344 行,只包含不同的电子邮件地址:

DataTable distinctTbl = dt.AsDataView().ToTable(true, "Email");

我正在尝试使用嵌套循环为每个电子邮件地址创建一个整数列表(客户 ID):

foreach (DataRow dr in distinctTbl.Rows)
{
    // for each email address:
    List<int> idNums = new List<int>();

    foreach (DataRow myRow in dt.Rows) 
    {
        // for every customerID / email pair in the original table
        if (myRow["Email"] == dr["Email"])
        {
            idNums.Add((int)myRow["CustomerID"]);
        }
    }

    // Do something with the List<int> before exiting outside loop
}

当我运行这段代码时,每个整数列表都包含一个值。该值是正确的,但每个电子邮件地址至少应该有两个。我已经进行了足够的调试以发现它始终正确识别第一个,但跳过任何后续匹配。我确定我遗漏了一些明显的东西,但是有人看到发生了什么吗?

【问题讨论】:

  • 如果您将内部循环 foreach (DataRow myRow in dt.Rows) 更改为 for loop 会怎样,因此声明类似列数 int iColCount = distinctTbl.Columns.Count; 然后遍历每一行的列.. 如果没有则声明一个var intCnt = 0;在内部 foreach 循环之外,然后检查是否myRow[intCnt]["Email] == (string)dr["Email"] 然后添加必要的项目

标签: c# loops datatable


【解决方案1】:

放弃foreach 循环。

您可以使用 Linq 更轻松地获取您要查找的信息。

Dictionary<string, List<int>> emailIDs =
    dt.Rows.OfType<DataRow>()
           .GroupBy(row => (string)row["Email"])
           .ToDictionary(grp => grp.Key,
                         grp => grp.Select(row => (int)row["CustomerID"]).ToList());

【讨论】:

  • 我喜欢!我最初尝试使用 Linq,但无法弄清楚以这种方式对所有内容进行分组的语法。这就像一个魅力,谢谢!
  • @ElementalPete GroupBy 方法起初可能有点棘手,因为它不像大多数 Linq 方法那样只返回普通的 IEnumerable 或单个元素。我觉得他们还需要更多 ToDictionary 重载以方便起见,但在这种情况下,现有的一个工作正常。
【解决方案2】:

一种快速简便的解决方案是使用Dictionary&lt;string,List&lt;int&gt;&gt; 而不是列表:

    Dictionary<string, List<int>> idNums = new Dictionary<string, List<int>>();
    foreach (DataRow myRow in dt.Rows)
    {
        string email = myRow["Email"].ToString()
        if (idNums.ContainsKey(email))
        {
            idNums[email].Add((int)myRow["CustomerID"]);
        }
        else
        {
            idNums.Add(email, new List<int> { (int)myRow["CustomerID"] });
        }
    }

现在 idNums 将包含与每封电子邮件关联的 id 列表。

【讨论】:

  • 我选择使用 Linq 查询,但这似乎也可以正常工作!感谢您的建议。
  • @ElementalPete - 要记住的一件事。由于您的数据量很小,因此效率可能不是最重要的问题。但是,如果数据量变得非常大,请记住 LINQ 查询会迭代数据两次,并且可能会对大型数据集造成性能影响。
猜你喜欢
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 2019-07-13
  • 2016-05-28
相关资源
最近更新 更多