【问题标题】:how to remove empty strings from list, then remove duplicate values from a list如何从列表中删除空字符串,然后从列表中删除重复值
【发布时间】:2012-08-08 14:45:44
【问题描述】:

假设我有一个来自表的一些列值的列表,我如何删除空字符串和重复值。请看以下代码:

List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();

这是我刚刚编写的代码,但是 Amiram 的代码更优雅,所以我会在这里选择答案:

DataTable dtReportsList = someclass.GetReportsList();

        if (dtReportsList.Rows.Count > 0)
       { 
           List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();
           dtList.RemoveAll(x=>x == "");
           dtList = dtList.Distinct().ToList();         

           rcboModule.DataSource = dtList;
           rcboModule.DataBind();               
           rcboModule.Items.Insert(0, new RadComboBoxItem("All", "All"));
       }

【问题讨论】:

  • 了解 RemoveAll() 会改变 dtList;删除的每个元素都会强制 List 重新排列它使用的底层数组中较高索引中的元素。像 Amiram 使用他的 Where 方法那样跳过它们会更快。

标签: c# linq


【解决方案1】:
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

我假设空字符串和空格就像 null。如果不是,您可以使用IsNullOrEmpty(允许空格)或s != null

【讨论】:

  • 只有一件事;使用 Distinct() 进行重复数据删除效率相对较低,因为该方法必须假设最坏的情况。
  • @KeithS 关于这些数据,我们知道哪些 Distinct 不知道的断言可以对其进行优化?
  • 我们可以对列表进行排序,然后断言它已排序,从而使重复数据删除算法成为线性的;看我的回答。
【解决方案2】:

Amiram 的回答是正确的,但是实现的 Distinct() 是一个 N2 操作;对于列表中的每个项目,算法将其与所有已处理的元素进行比较,如果它是唯一的则返回它,否则忽略它。我们可以做得更好。

排序的列表可以在线性时间内进行重复数据删除;如果当前元素等于前一个元素,则忽略它,否则返回它。排序是 NlogN,所以即使必须对集合进行排序,我们也会得到一些好处:

public static IEnumerable<T> SortAndDedupe<T>(this IEnumerable<T> input)
{
   var toDedupe = input.OrderBy(x=>x);

   T prev;
   foreach(var element in toDedupe)
   {
      if(element == prev) continue;

      yield return element;
      prev = element;      
   }
}

//Usage
dtList  = dtList.Where(s => !string.IsNullOrWhitespace(s)).SortAndDedupe().ToList();

这会返回相同的元素;他们只是排序。

【讨论】:

  • 太棒了。如果我没记错的话,通过迭代你实际上是在执行排序的元素。你能想出一种方法让你的方法“懒惰”吗?
  • 不幸的是,大多数排序都需要了解要排序的整个集合;最后一个元素可能是第一个需要返回的元素。因此,必须评估输入的所有元素以产生输出的第一个元素。在找到其输出的下一个元素后,我能想到的唯一一种可能会被中断的是 SelectionSort 变体,在这种情况下,我们又回到了开始的地方。
  • 此外,在我们的例子中,整个操作的结果是一个列表,需要“急切”地开始执行。如果我们想将它作为 IEnumerable 使用并推迟执行,您可以将函数的核心放在实现 IEnumerable 的隐藏 Iterator 类中。
  • Distinct 使用散列,应该更接近 O(N) 而不是 O(N^2)。 source
  • ...好吧,我会被诅咒的,确实如此; System.Linq.Set 是 Distinct 使用的内部哈希表实现,假设您的项目的 GetHashCode() 实现高效并产生均匀分布的哈希(默认实现会这样做)。但是哈希表确实存在内存问题。 .NET 的基本实现使用两个数组,一个是整数,另一个是链接项,每个数组最多等于集合中的项数,最坏的情况是它的两倍。
【解决方案3】:

简化Amiram Korach's解决方案:

dtList.RemoveAll(s => string.IsNullOrWhiteSpace(s))

无需使用 Distinct() 或 ToList()

【讨论】:

    【解决方案4】:

    Amiram Korach 解决方案确实很整洁。为了通用性,这里有一个替代方案。

    var count = dtList.Count;
    // Perform a reverse tracking.
    for (var i = count - 1; i > -1; i--)
    {
        if (dtList[i]==string.Empty) dtList.RemoveAt(i);
    }
    // Keep only the unique list items.
    dtList = dtList.Distinct().ToList();
    

    【讨论】:

    • 虽然这可行,但 Where 子句更快,因为它不必改变输入集合。您正在最大限度地减少从列表中删除元素时必须执行的“移位”次数,但 Where 不会从输入中删除任何内容;它只是跳过不匹配的元素。
    猜你喜欢
    • 1970-01-01
    • 2019-05-17
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    • 2019-06-01
    • 2011-12-17
    • 2011-04-20
    相关资源
    最近更新 更多