Фильтрация списков с помощью LINQ - PullRequest
17 голосов
/ 06 января 2009

У меня есть список людей, которые возвращаются из внешнего приложения, и я создаю список исключений в своем локальном приложении, чтобы дать мне возможность вручную удалять людей из списка.

У меня есть составной ключ, который я создал, который является общим для обоих, и я хочу найти эффективный способ удаления людей из моего Списка с помощью моего Списка

например,

class Person
{
    prop string compositeKey { get; set; }
}

class Exclusions
{
    prop string compositeKey { get; set; }
}

List<Person> people = GetFromDB;

List<Exclusions> exclusions = GetFromOtherDB;

List<Person> filteredResults = People - exclustions using the composite key as a comparer

Я думал, что LINQ - это идеальный способ сделать это, но после попытки объединений, методов расширения, использования выходов и т. Д. У меня все еще возникают проблемы.

Если бы это был SQL, я бы использовал not in (?,?,?) запрос.

Ответы [ 9 ]

30 голосов
/ 06 января 2009

Посмотрите на метод Кроме , который вы используете следующим образом:

var resultingList = 
    listOfOriginalItems.Except(listOfItemsToLeaveOut, equalityComparer)

Возможно, вы захотите использовать перегрузку, с которой я связан, что позволит вам указать собственный IEqualityComparer. Таким образом, вы можете указать, как элементы соответствуют на основе вашего составного ключа. (Если вы уже переопределили Equals, вам не нужен IEqualityComparer.)

Редактировать: Поскольку кажется, что вы используете два разных типа классов, вот еще один способ, который может быть проще. Предполагая, что List<Person> называется persons, а List<Exclusion> называется exclusions:

var exclusionKeys = 
        exclusions.Select(x => x.compositeKey);
var resultingPersons = 
        persons.Where(x => !exclusionKeys.Contains(x.compositeKey));

Другими словами: выберите из исключений только ключи, затем выберите из лиц все объекты Person, у которых нет ни одного из этих ключей.

4 голосов
/ 06 января 2009

Я бы просто использовал метод FindAll в классе List. i.e.:

List<Person> filteredResults = 
    people.FindAll(p => return !exclusions.Contains(p));

Не уверен, что синтаксис будет точно соответствовать вашим объектам, но я думаю, вы можете видеть, куда я иду с этим.

2 голосов
/ 08 января 2009

Большое спасибо за это, ребята.

Мне удалось свести это к одной строке:

  var results = from p in People 
                where !(from e in exclusions 
                        select e.CompositeKey).Contains(p.CompositeKey) 
                select p;

Еще раз спасибо всем.

2 голосов
/ 06 января 2009

Вы можете использовать метод расширения «Кроме» (см. http://msdn.microsoft.com/en-us/library/bb337804.aspx)

В вашем коде

var difference = people.Except(exclusions);
1 голос
/ 02 мая 2018
var thisList = new List<string>{ "a", "b", "c" };
var otherList = new List<string> {"a", "b"};

var theOnesThatDontMatch = thisList
        .Where(item=> otherList.All(otherItem=> item != otherItem))
        .ToList();

var theOnesThatDoMatch = thisList
        .Where(item=> otherList.Any(otherItem=> item == otherItem))
        .ToList();

Console.WriteLine("don't match: {0}", string.Join(",", theOnesThatDontMatch));
Console.WriteLine("do match: {0}", string.Join(",", theOnesThatDoMatch));

//Output:
//don't match: c
//do match: a,b

Адаптируйте типы списков и лямбды соответственно, и вы сможете отфильтровать все что угодно.

https://dotnetfiddle.net/6bMCvN

1 голос
/ 06 января 2009

Я не мог понять, как это сделать в чистом MS LINQ, поэтому я написал свой собственный метод расширения, чтобы сделать это:

public static bool In<T>(this T objToCheck, params T[] values)
{
    if (values == null || values.Length == 0) 
    {
        return false; //early out
    }
    else
    {
        foreach (T t in values)
        {
            if (t.Equals(objToCheck))
                return true;   //RETURN found!
        }

        return false; //nothing found
    }
}
0 голосов
/ 17 декабря 2017
            var result = Data.Where(x =>
            {
            bool condition = true;
            double accord = (double)x[Table.Columns.IndexOf(FiltercomboBox.Text)];
            return condition && accord >= double.Parse(FilterLowertextBox.Text) && accord <= double.Parse(FilterUppertextBox.Text); 
        });
0 голосов
/ 06 января 2009

Этот LINQ ниже генерирует SQL для левого внешнего соединения и затем принимает все результаты, которые не находят совпадений в вашем списке исключений.

List<Person> filteredResults =from p in people
        join e in exclusions on p.compositeKey equals e.compositeKey into temp
        from t in temp.DefaultIfEmpty()
        where t.compositeKey == null
        select p

Дайте мне знать, если это работает!

0 голосов
/ 06 января 2009

Я бы сделал что-то подобное, но держу пари, что есть более простой способ. Я думаю, что SQL из linqtosql будет использовать выбор от человека, где НЕ СУЩЕСТВУЕТ (выберите из списка исключений)

static class Program
{
    public class Person
    {
        public string Key { get; set; }
        public Person(string key)
        {
           Key = key;
        }
    }
    public class NotPerson
    {
        public string Key { get; set; }
        public NotPerson(string key)
        {
           Key = key;
        }
    }
    static void Main()
    {

       List<Person> persons = new List<Person>()
       { 
           new Person ("1"),
           new Person ("2"),
           new Person ("3"),
           new Person ("4")
       };

       List<NotPerson> notpersons = new List<NotPerson>()
       { 
           new NotPerson ("3"),
           new NotPerson ("4")
       };

       var filteredResults = from n in persons
                             where !notpersons.Any(y => n.Key == y.Key)
                             select n;

       foreach (var item in filteredResults)
       {
          Console.WriteLine(item.Key);
       }
    }
 }
...