як видалити порожні рядки зі списку, а потім видалити повторювані значення зі списку


82

Скажімо, у мене є список деяких значень стовпців, що надходять із таблиці, як мені видалити порожні рядки та повторювані значення. Перегляньте наступний код:

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; кожен вилучений елемент змушує Список переставляти елементи у вищі індекси в базовому масиві, який він використовує. Було б швидше просто пропустити їх, як це робить Амірам з його методом Де.
KeithS

Відповіді:


201
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

Я припустив, що порожній рядок і пробіли - як нуль. Якщо ні, ви можете використовувати IsNullOrEmpty(дозволити пробіли) абоs != null


Тільки одне; дедупція за допомогою Distinct () є відносно неефективною, оскільки метод повинен передбачати найгірший випадок.
KeithS

@KeithS Які твердження ми знаємо про ці дані, а які Distinctне дозволяють їх оптимізувати?
Серві

Ми можемо сортувати список, а потім стверджувати, що він відсортований, роблячи алгоритм дедупції лінійним; див. мою відповідь.
KeithS

9

Відповідь Amiram правильна, але Distinct (), як реалізовано, є операцією N 2 ; для кожного елемента у списку алгоритм порівнює його з усіма вже обробленими елементами та повертає, якщо він унікальний, або ігнорує, якщо ні. Ми можемо зробити краще.

Відсортований список можна deduped в лінійний час; якщо поточний елемент дорівнює попередньому елементу, ігноруйте його, інакше поверніть його. Сортування - це 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();

Це повертає ті самі елементи; їх просто сортують.


Чудово. Якщо я не помиляюся, повторюючи елементи, які ви насправді виконуєте впорядкування. Чи можете ви придумати спосіб зробити свій метод «лінивим»?
Amiram Korach

На жаль, більшість сортів вимагають знання всієї колекції для сортування; останній елемент може бути першим, який потрібно повернути. Отже, всі елементи вхідних даних повинні бути оцінені, щоб отримати перший елемент вихідних даних. Єдине сортування, про яке я можу думати, яке може бути перерване після пошуку наступного елемента його результату, це варіант SelectionSort, і в такому випадку ми повернулися з того, з чого почали.
KeithS

Крім того, у нашому випадку результатом всієї операції є список, для початку якого потрібно "нетерпляче" виконання. Якщо ми хотіли працювати з ним як з IEnumerable і відкласти його виконання, ви можете взяти м'ясо функції та помістити його в прихований клас Iterator, який реалізує IEnumerable.
KeithS

Distinctвикористовує хешування і має бути ближчим до O (N), ніж O (N ^ 2). джерело
Ризикований Мартін

... Ну, я буду проклятий, це справді; System.Linq.Set внутрішній Хеш реалізація використовується Distinct, який буде поруч з O (1) час доступу за умови реалізації GetHashCode () ваших деталей є ефективним і виробляє рівномірно розподілений хеш (реалізація за замовчуванням буде робити це) . Однак у хеш-таблиці є проблеми з пам'яттю; Основна реалізація .NET використовує два масиви, один з ints та інший зв'язаних елементів, кожен з яких у кращому випадку дорівнює кількості елементів у наборі, а в гіршому - вдвічі більше.
KeithS

1

Розчин 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();

4
Хоча це і буде працювати, речення Where швидше, оскільки йому не потрібно мутувати вхідну колекцію. Ви мінімізуєте кількість "змін", які необхідно виконати під час вилучення елементів зі списку, але де не видаляє нічого із вводу; він просто пропускає елементи, які не збігаються.
KeithS

0

Щоб спростити рішення Amiram Korach :

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

Не потрібно використовувати Distinct () або ToList ()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.