Подтвердить что ты не робот

Использование Linq для удаления из набора, где ключ существует в другом наборе?

Каков правильный способ выполнения вычитания с помощью Linq? У меня есть список из 8000+ банков, где я хочу удалить часть из них на основе номера маршрутизации. Часть находится в другом списке, а номер маршрутизации - это свойство ключа для обоих. Вот упрощение:

public class Bank
{
    public string RoutingNumber { get; set; }
    public string Name { get; set; }
}

var removeThese = new List<string>() { "111", "444", "777" };

var banks = new List<Bank>()
{
    new Bank() { RoutingNumber = "111", Name = "First Federal" },
    new Bank() { RoutingNumber = "222", Name = "Second Federal" },
    new Bank() { RoutingNumber = "333", Name = "Third Federal" },
    new Bank() { RoutingNumber = "444", Name = "Fourth Federal" },
    new Bank() { RoutingNumber = "555", Name = "Fifth Federal" },
    new Bank() { RoutingNumber = "666", Name = "Sixth Federal" },
    new Bank() { RoutingNumber = "777", Name = "Seventh Federal" },
    new Bank() { RoutingNumber = "888", Name = "Eight Federal" },
    new Bank() { RoutingNumber = "999", Name = "Ninth Federal" },
};

var query = banks.Remove(banks.Where(x => removeThese.Contains(x.RoutingNumber)));
4b9b3361

Ответ 1

Это должно сделать трюк:

var toRemove = banks.Where(x => removeThese.Contains(x.RoutingNumber)).ToList();
var query = banks.RemoveAll(x => toRemove.Contains(x));

Первый шаг - убедиться, что вам не нужно повторно запускать этот первый запрос снова и снова, когда изменяется banks.

Это тоже должно работать:

var query = banks.Except(toRemove);

как вторая строка.

ИЗМЕНИТЬ

Тим Шмельтер указал, что для работы Except вам необходимо переопределить Equals и GetHashCode.

Итак, вы можете реализовать его так:

public override string ToString()
{
   ... any serialization will do, for instance JSON or CSV or XML ...
   ... OR any serialization that identifies the object quickly, such as:
   return "Bank: " + this.RoutingNumber;
}


public override bool Equals(System.Object obj)
{
    return ((obj is Bank) && (this.ToString().Equals(obj.ToString()));
}


public override int GetHashCode()
{
    return this.ToString().GetHashCode();
}

Ответ 2

Как правило, это меньше работает, чтобы просто вытащить те, которые вам нужны, а не удалять те, которые вы не делаете.

var query = myList.Where(x => !removeThese.Contains(x.RoutingNumber));

Ответ 3

Фильтрация этого типа обычно выполняется с помощью общих конструкций LINQ:

banks = banks.Where(bank => !removeThese.Contains(bank.RoutingNumber)).ToList();

В этом конкретном случае вы также можете использовать List<T>.RemoveAll, чтобы выполнить фильтрацию на месте, что будет быстрее:

banks.RemoveAll(bank => removeThese.Contains(bank.RoutingNumber));

Кроме того, по соображениям производительности, если количество номеров маршрутизации для удаления велико, вы должны рассмотреть возможность размещения их в HashSet<string>.

Ответ 4

Либо используйте методы расширения Linq Where и ToList для создания нового списка или используйте List.RemoveAll, что является более эффективным с тех пор он изменяет исходный список:

banks = banks.Where(x => !removeThese.Contains(x.RoutingNumber)).ToList();
banks.RemoveAll(x => removeThese.Contains(x.RoutingNumber));

Конечно, вам нужно отменить условие, так как первое сохраняет, что Where уходит, а второе удаляет то, что возвращает предикат в RemoveAll.

Ответ 5

Пробовали ли вы использовать RemoveAll()?

var query = banks.RemoveAll(p => removeThese.Contains(p.RoutingNumber));

Это приведет к удалению любых значений из banks, где соответствующая запись присутствует в removeThese.

query будет содержать количество записей, удаленных из списка.

Примечание. Оригинальная переменная banks будет обновляться непосредственно этим запросом; переназначение не требуется.

Ответ 6

Вы можете использовать RemoveAll()

var removedIndexes = banks.RemoveAll(x => removeThese.Contains(x.RoutingNumber));

или

banks = banks.Where(bank => !removeThese.Contains(bank.RoutingNumber)).ToList();