Csharp list contains list

Use LINQ to get items in one List<>, that are not in another List<>

I would assume there’s a simple LINQ query to do this, I’m just not exactly sure how. Given this piece of code:

class Program < static void Main(string[] args) < ListpeopleList1 = new List(); peopleList1.Add(new Person() < >); peopleList1.Add(new Person() < >); peopleList1.Add(new Person() < >); List peopleList2 = new List(); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); > > class Person < public int ID < get; set; >> 

I would like to perform a LINQ query to give me all of the people in peopleList2 that are not in peopleList1 . This example should give me two people (ID = 4 &

Perhaps it’s a good idea to make ID readonly since the identity of an object shouldn’t change over its live time. Unless of course your testing- or ORM-framework requires it to be mutable.

11 Answers 11

This can be addressed using the following LINQ expression:

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

An alternate way of expressing this via LINQ, which some developers find more readable:

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

Warning: As noted in the comments, these approaches mandate an O(n*m) operation. That may be fine, but could introduce performance issues, and especially if the data set is quite large. If this doesn’t satisfy your performance requirements, you may need to evaluate other options. Since the stated requirement is for a solution in LINQ, however, those options aren’t explored here. As always, evaluate any approach against the performance requirements your project might have.

@nikie, the OP asked for a solution that uses Linq. Maybe he’s trying to learn Linq. If the question had been for the most efficient way, my question would not necessarily have been the same.

Читайте также:  Счетчик для index php

This is equivalent and I find easier to follow: var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

@Menol — it might be a bit unfair to criticize someone who correctly responds to a question. People shouldn’t need to anticipate all the ways and contexts that future people might stumble onto the answer. In reality, you should direct that to nikie — who took the time to state that they knew of an alternative without providing it.

If you override the equality of People then you can also use:

peopleList2.Except(peopleList1) 

Except should be significantly faster than the Where(. Any) variant since it can put the second list into a hashtable. Where(. Any) has a runtime of O(peopleList1.Count * peopleList2.Count) whereas variants based on HashSet (almost) have a runtime of O(peopleList1.Count + peopleList2.Count) .

Except implicitly removes duplicates. That shouldn’t affect your case, but might be an issue for similar cases.

Or if you want fast code but don’t want to override the equality:

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

This variant does not remove duplicates.

That’s why I wrote that you need to override the equality. But I’ve added an example which works even without that.

It would also work if Person was a struct. As it is though, Person seems an incomplete class as it has a property called «ID» which does not identify it — if it did identify it, then equals would be overridden so that equal ID meant equal Person. Once that bug in Person is fixed, this approach is then better (unless the bug is fixed by renaming «ID» to something else that doesn’t mislead by seeming to be an identifier).

It also works great if you’re talking about a list of strings (or other base objects), which was what I was searching for when I came upon this thread.

@DanKorn Same, this a simpler solution, compared to the where, for basic comparison, int, objects ref, strings.

Or if you want it without negation:

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

Basically it says get all from peopleList2 where all ids in peopleList1 are different from id in peoplesList2.

Just a little bit different approach from the accepted answer 🙂

This might be faster just because it is lazy. Note that this is not doing any real work just yet. It’s not until you enumerate the list that it actually does the work (by calling ToList or using it as part of a foreach loop, etc.)

Since all of the solutions to date used fluent syntax, here is a solution in query expression syntax, for those interested:

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

I think it is different enough from the answers given to be of interest to some, even thought it most likely would be suboptimal for Lists. Now for tables with indexed IDs, this would definitely be the way to go.

Bit late to the party but a good solution which is also Linq to SQL compatible is:

List list1 = new List() < "1", "2", "3" >; List list2 = new List() < "2", "4" >; List 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(); List 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(); List 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(); 

This Enumerable Extension allow you to define a list of item to exclude and a function to use to find key to use to perform comparison.

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

Create a new class somewhere with the EnumerableExtensions code in Bertrand’s reply. Add using statement in class where query is performed. Then change the selection code to var result = peopleList2.Exclude(peopleList1, i => i.ID);

Klaus’ answer was great, but ReSharper will ask you to «Simplify LINQ expression»:

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

It’s worth to note that this trick won’t work if there’s more than one property binding the two objects (think SQL composite key).

Alrekr — If what you mean to say is «you will need to compare more properties if more properties need comparing» then I’d say that’s pretty obvious.

Once you write a generic FuncEqualityComparer you can use it everywhere.

peopleList2.Except(peopleList1, new FuncEqualityComparer((p, q) => p.ID == q.ID)); public class FuncEqualityComparer : IEqualityComparer  < private readonly Funccomparer; private readonly Func hash; public FuncEqualityComparer(Func 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); > 

first, extract ids from the collection where condition

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

second, use «compare» estament to select ids diffent to the selection

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

Obviously you can use x.key != «TEST», but is only a example

Here is a working example that get IT skills that a job candidate does not already have.

//Get a list of skills from the Skill table IEnumerable skillenum = skillrepository.Skill; //Get a list of skills the candidate has IEnumerable 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 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); 
 < static void Main(string[] args) < ListpeopleList1 = new List(); peopleList1.Add(new Person() < >); peopleList1.Add(new Person() < >); peopleList1.Add(new Person() < >); List peopleList2 = new List(); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); peopleList2.Add(new Person() < >); > var leftPeeps = peopleList2.Where(x => !peopleList1.Select(y => y.ID).Contains(x.ID))?.ToList() ?? new List(); > class Person < public int ID < get; set; >> 

Notice the !peopleList1.Select(y => y.ID).Contains(x.ID) Select statement. This allows us to grab the indexer we want (ID) and see if it contains the ID of the previous list. ! means we don’t want those. This may leave us with no entries. so, we can ensure we have something by checking for null and using a null coalesce.

Источник

Csharp list contains list

This forum has migrated to Microsoft Q&A. Visit Microsoft Q&A to post new questions.

Visual studio home link

Answered by:

Question

Hi,
I am using VS2008 and starting out with linq and wish to know if there is a simple (existing) way to do the following:

I have two lists (actually one is an IEnumerable, but this can be changed) of Guids and want to check if one list contains any of the guids in the other list (and to stop as soon as it finds out). I dont know if linq is the ‘best’ way to do so, but thought that there would be some existing extension etc that would do this..

as always it would be good to know the fastest (and most readable!) way to do this 🙂

Answers

If you have a list, which is an IEnumerable, and a list2, which is a List, with Linq, you can do it like this:

bool containsCommonItem = list.Any(x => list2.Contains(x));

If both are IEnumerable, you can use a nested Any to figure this out:

list.Any(x => list2.Any(y => x.Equals(y)));

The «fastest» way might not be (is probably not) LINQ however, it might actually be faster to use a for loop, or a foreach loop or something, but only performance testing all of the options will give you that information.

I’d set up a series of tests (using the Stopwatch class) to figure out which method is the fastest. Coding Light — Illuminated Ideas and Algorithms in Software
Coding Light Wiki • LinkedIn • ForumsBrowser

All replies

If you have a list, which is an IEnumerable, and a list2, which is a List, with Linq, you can do it like this:

bool containsCommonItem = list.Any(x => list2.Contains(x));

If both are IEnumerable, you can use a nested Any to figure this out:

list.Any(x => list2.Any(y => x.Equals(y)));

The «fastest» way might not be (is probably not) LINQ however, it might actually be faster to use a for loop, or a foreach loop or something, but only performance testing all of the options will give you that information.

I’d set up a series of tests (using the Stopwatch class) to figure out which method is the fastest. Coding Light — Illuminated Ideas and Algorithms in Software
Coding Light Wiki • LinkedIn • ForumsBrowser

thanks, I was close, but made a small (too embarrassing!) mistake.

I thought Any extension was fast enough i.e. would stop when it found one condition that matched i.e. if doing:

then it would stop on first match..

As said, I guess stopwatch is the way to go but linq is readable and its not as if dealing with 000s in the list;

I would argue that the most efficient method of completing such a task depends on the data-set itself. Though, since this question is more a functional programming paradigm than Object Oriented (imperitive) I think it would be better answered in the SQL forums. Ignoring duplication, perhaps you can start with (in T-SQL, somebody else can translate this to LINQ):

SELECT * FROM List1 Where in List2

and iterate the resultant dataset. おろ?

I would argue that the most efficient method of completing such a task depends on the data-set itself. Though, since this question is more a functional programming paradigm than Object Oriented (imperitive) I think it would be better answered in the SQL forums. Ignoring duplication, perhaps you can start with (in T-SQL, somebody else can translate this to LINQ):

SELECT * FROM List1 Where in List2

and iterate the resultant dataset. おろ?

yep but I already have the data sets, the lists of guids, and dont care how many of them ‘overlap’ just that at least one is contained in the other; therefore no need to iterate the resultset from above in this case;
Just wanted to know the ‘linq’ way for this basic example, and thought it would be more readable than having to use a utility or anon delegate to do the work I wanted;
cheers,
km

Источник

Оцените статью