Используйте LINQ для получения элементов в одном списке <>, которые не находятся в другом списке <>

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

class Program
{
    static void Main(string[] args)
    {
        List<Person> peopleList1 = new List<Person>();
        peopleList1.Add(new Person() { ID = 1 });
        peopleList1.Add(new Person() { ID = 2 });
        peopleList1.Add(new Person() { ID = 3 });

        List<Person> peopleList2 = new List<Person>();
        peopleList2.Add(new Person() { ID = 1 });
        peopleList2.Add(new Person() { ID = 2 });
        peopleList2.Add(new Person() { ID = 3 });
        peopleList2.Add(new Person() { ID = 4 });
        peopleList2.Add(new Person() { ID = 5 });
    }
}

class Person
{
    public int ID { get; set; }
}

Я хотел бы выполнить запрос LINQ, чтобы дать мне всех людей в peopleList2, которых нет в peopleList1 этом примере должны быть peopleList1 два человека (ID = 4 & ID = 5)

Ответ 1

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

Ответ 2

Если вы переопределите равенство людей, вы также можете использовать:

peopleList2.Except(peopleList1)

Except должен быть значительно быстрее, чем вариант Where(...Any), поскольку он может помещать второй список в хэш-таблицу. Where(...Any) имеет время выполнения O(peopleList1.Count * peopleList2.Count), тогда как варианты, основанные на HashSet<T> (почти), имеют время выполнения O(peopleList1.Count + peopleList2.Count).

Except неявно удаляет дубликаты. Это не должно влиять на ваше дело, но может быть проблемой для подобных случаев.

Или если вы хотите быстрый код, но не хотите переопределять равенство:

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID));
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID));

Этот вариант не удаляет дубликаты.

Ответ 3

Или если вы хотите его без отрицания:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

В основном это говорит, что получить все от peopleList2, где все идентификаторы в peopleList1 отличаются от id в peopleList2.

Совсем другой подход от принятого ответа:)

Ответ 4

Поскольку все решения на сегодняшний день используют свободный синтаксис, вот решение синтаксиса выражения запроса для заинтересованных:

var peopleDifference = 
  from person2 in peopleList2
  where !(
      from person1 in peopleList1 
      select person1.ID
    ).Contains(person2.ID)
  select person2;

Я думаю, что он отличается от ответов, которые могут быть интересны некоторым, даже считал, что он скорее всего будет субоптимальным для списков. Теперь для таблиц с индексированными идентификаторами это определенно будет способом.

Ответ 5

Бит опоздал на вечеринку, но хорошим решением, совместимым с Linq to SQL, является:

List<string> list1 = new List<string>() { "1", "2", "3" };
List<string> list2 = new List<string>() { "2", "4" };

List<string> inList1ButNotList2 = (from o in list1
                                   join p in list2 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inList2ButNotList1 = (from o in list2
                                   join p in list1 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inBoth = (from o in list1
                       join p in list2 on o equals p into t
                       from od in t.DefaultIfEmpty()
                       where od != null
                       select od).ToList<string>();

Престижность http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C

Ответ 6

Клаус ответил великолепно, но ReSharper попросит вас "упростить выражение LINQ":

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

Ответ 7

Это Enumerable Extension позволяет вам определить список элементов для исключения и функцию, используемую для поиска ключа, который будет использоваться для сравнения.

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source,
    IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector)
    {
       var excludedSet = new HashSet<TKey>(exclude.Select(keySelector));
       return source.Where(item => !excludedSet.Contains(keySelector(item)));
    }
}

Вы можете использовать его таким образом

list1.Exclude(list2, i => i.ID);

Ответ 8

Вот рабочий пример, который позволяет ИТ-специалистам, чтобы кандидат на работу не уже есть.

//Get a list of skills from the Skill table
IEnumerable<Skill> skillenum = skillrepository.Skill;
//Get a list of skills the candidate has                   
IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill
       .Where(p => p.Candidate_ID == Candidate_ID);             
//Using the enum lists with LINQ filter out the skills not in the candidate skill list
IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID));
//Assign the selectable list to a viewBag
ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1);

Ответ 9

во-первых, извлечь идентификаторы из коллекции, где условие

List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();

во-вторых, используйте положение "сравнить", чтобы выбрать идентификаторы, различающиеся для выбора

List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();

Очевидно, вы можете использовать x.key! = "TEST", но это только пример

Ответ 10

После того, как вы напишите общий FuncEqualityComparer, вы сможете использовать его везде.

peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));

public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, int> hash;

    public FuncEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
        if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
            hash = (_) => 0;
        else
            hash = t => t.GetHashCode(); 
    }

    public bool Equals(T x, T y) => comparer(x, y);
    public int GetHashCode(T obj) => hash(obj);
}