2016-02-28 4 views
6

У меня есть 2 списка, имена которых являются listA и listB.Как удалить строки из списка из другого списка?

Я хочу, чтобы удалить строки в LISTB, которые находятся в lišta, но я хочу сделать это таким образом:

если lišta содержит: «бар», «бар», «бар», «Foo» и LISTB содержит: «бар»

удаляет только 1 бар и результат будет: «бар», «бар», «Foo»

код, который я написал удаляет все «бар»:

List<string> result = listA.Except(listB).ToList(); 
+0

ли сохранить некоторые из первоначального списка в порядке материи? – hatchet

ответ

5

Вы можете попытаться удалить его один за другим:

foreach (var word in listB) 
    listA.Remove(word); 

Метод Remove удалит только один элемент, в то время и не кидает исключение (но возвращение ложным), если элемент не найден: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx

+0

Вы можете избежать вызова Содержит, используя непосредственно IndexOf, чтобы получить позицию – Steve

+1

Это неэффективно, но вы можете сделать это, по крайней мере, напрямую используя 'listA.Remove (word)'. Не требуется «Содержит». –

+0

@IvanStoev вправо, просто обновите его – Ian

3
var listA = new List<string>() { "bar", "bar", "bar", "foo" }; 
var listB = new List<string>() { "bar" }; 

foreach (var word in listB){ 
    listA.Remove(word); 
} 
+0

Это, в основном, копия ответа @Ian, размещенного за 10 минут до вашего ответа –

+0

да, это правда, я поддержал его ответ – csa

1

Это более быстрый метод, но он, вероятно, изменит порядок элементов первого списка. Шаги:

  • Сопоставьте lišta к Dictionary<string, int> (назовем его listAMap), где ключ является элементом списка и значение представляет собой общее число раз, когда значение имеет место в lišta;
  • Итерации через listB и для каждого элемента спискаB, если этот элемент находится в listAMap, уменьшите его количество;
  • Получите ключи от listMapA, используя Keys property словарей C#, и выполните итерацию по всем клавишам. Для каждого ключа, который имеет положительное значение, добавьте этот ключ в другой список в общей сумме его отсчетов. Поэтому, если запись "bar" -> 2, добавьте дважды «бар» в новый список.

Общее время работы алгоритма является О (т + п), где т и п число элементов в обоих исходных списках. Это лучшее время работы, чем другие упомянутые здесь подходы, которые имеют O (m * n) время работы. Очевидно, что этот алгоритм использует больше пространства.


Поддерживающая Код для алгоритма выше:

//Step-1: Create the dictionary... 
var listAMap = new Dictionary<string, int>(); 
foreach (var listAElement in listA) 
{ 
    listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1); 
} 

// Step-2: Remove the listB elements from dictionary... 
foreach (var listBElement in listB) 
{ 
    if (listAMap.Contains(listBElement)) listAMap[listBElement]--; 
} 

//Step-3: Create the new list from pruned dictionary... 
var prunedListA = new List<string>(); 
foreach (var key in listAMap.Keys) 
{ 
    if (listAMap[key] <= 0) continue; 
    for (var count = 0; count < listAMap[key]; count++) 
    { 
     prunedListA.Add(key); 
    } 
} 

//prunedListA contains the desired elements now. 
+0

Я думал о чем-то подобном, но считал listB, а затем удалял элементы из списка listA tah (и уменьшение количества совпадений). Во всяком случае, +1 за размышление об эффективности. –

+0

@IvanStoev: Мы не сопоставляем элементы в списке.Мы делаем поиск O (1) в словаре. Серьезно, решение очень просто (не то, что вы можете дать +2 на ответ). Я должен был добавить код тоже. Будет делать, когда я получаю доступ к SO с ноутбука. – displayName

+0

@IvanStoev: Последнее, что нужно сделать сейчас, - это то, что приведенный выше код можно разделить на отдельный метод, чтобы его очиститель. – displayName

1

Вот более эффективный способ сделать это:

var countB = new Dictionary<string, int>(listB.Count); 
foreach (var x in listB) 
{ 
    int count; 
    countB.TryGetValue(x, out count); 
    countB[x] = count + 1; 
} 
listA.RemoveAll(x => 
{ 
    int count; 
    if (!countB.TryGetValue(x, out count)) return false; 
    if (count == 1) 
     countB.Remove(x); 
    else 
     countB[x] = count - 1; 
    return true; 
}); 
+0

Вы пропустили шаг, на котором вы заполняете * countB *. – displayName

+0

@displayName Я не сделал - попробуй и посмотри (подсказка - маленькая строка 'countB [x] = count + 1;') :) –

+0

О, я вижу ... не знал об этом поведении 'TryGetValue()' в словарях. Узнал что-то новое. – displayName

Смежные вопросы