Remove items from one list in another - c#

I'm trying to figure out how to traverse a generic list of items that I want to remove from another list of items.
So let's say I have this as a hypothetical example
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
I want to traverse list1 with a foreach and remove each item in List1 which is also contained in List2.
I'm not quite sure how to go about that as foreach is not index based.

You can use Except:
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
List<car> result = list2.Except(list1).ToList();
You probably don't even need those temporary variables:
List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();
Note that Except does not modify either list - it creates a new list with the result.

You don't need an index, as the List<T> class allows you to remove items by value rather than index by using the Remove function.
foreach(car item in list1) list2.Remove(item);

In my case I had two different lists, with a common identifier, kind of like a foreign key.
The second solution cited by "nzrytmn":
var result = list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();
Was the one that best fit in my situation.
I needed to load a DropDownList without the records that had already been registered.
Thank you !!!
This is my code:
t1 = new T1();
t2 = new T2();
List<T1> list1 = t1.getList();
List<T2> list2 = t2.getList();
ddlT3.DataSource= list2.Where(s => !list1.Any(p => p.Id == s.ID)).ToList();
ddlT3.DataTextField = "AnyThing";
ddlT3.DataValueField = "IdAnyThing";
ddlT3.DataBind();

I would recommend using the LINQ extension methods. You can easily do it with one line of code like so:
list2 = list2.Except(list1).ToList();
This is assuming of course the objects in list1 that you are removing from list2 are the same instance.

list1.RemoveAll(l => list2.Contains(l));

You could use LINQ, but I would go with RemoveAll method. I think that is the one that better expresses your intent.
var integers = new List<int> { 1, 2, 3, 4, 5 };
var remove = new List<int> { 1, 3, 5 };
integers.RemoveAll(i => remove.Contains(i));

Solution 1 : You can do like this :
List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();
But in some cases may this solution not work. if it is not work you can use my second solution .
Solution 2 :
List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
we pretend that list1 is your main list and list2 is your secondry list and you want to get items of list1 without items of list2.
var result = list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();

As Except does not modify the list, you can use ForEach on List<T>:
list2.ForEach(item => list1.Remove(item));
It may not be the most efficient way, but it is simple, therefore readable, and it updates the original list (which is my requirement).

I think it would be quick to convert list A to a dictionary and then foreach the second list and call DictA.Remove(item) otherwise I think most solutions will cause many iterations through list A either directly or under the covers.
If the lists are small, it probably won't matter.

In case you have two different list with different DataModals
List<FeedbackQuestionsModel> feedbackQuestionsList = new();
List<EmployeesFeedbacksQuestionsModel> employeeQuestionsList = new();
var resultList = feedbackQuestionsList.Where(p => !employeeQuestionsList.Any(x => x.Question == p.Question)).ToList();
feedbackQuestionsList = resultList.ToList();

Here ya go..
List<string> list = new List<string>() { "1", "2", "3" };
List<string> remove = new List<string>() { "2" };
list.ForEach(s =>
{
if (remove.Contains(s))
{
list.Remove(s);
}
});

Related

Unable to subtract a list from another list using .Except

I have the following code inside my asp.net mvc-5 & ET-6 :-
List<String> ScannedResourceNames = new List<String>();
if (scaninfo.Any(a => a.VMList.Any(a2 => a2.Resource.RESOURCENAME.ToLower() == vmname.ToLower())))
{
ScannedResourceNames.Add(vmname.ToLower());
}
List<String> allcurrentresourcename = scaninfo.SelectMany(a => a.VMList.Select(a2 => a2.Resource.RESOURCENAME)).ToList();
List<String> finallist = allcurrentresourcename.Except(ScannedResourceNames).ToList();
currently the allcurrentresourcename have 3 items :
A
B
C
while ScannedResourceName have 1 item :
B
So i though when i define .Except i will get 2 items (A& C) in the finallist var. but the final list will have 3 items (A,B,C). so can anyone advice on this please ?
You can use one of the standard StringComparer properties to ignore case.
List<String> finallist = all.Except(these, StringComparer.InvariantCultureIgnoreCase)
.ToList();
Probably related to case, because when you fill the ScannedResourceNames list you're ignoring it:
a2.Resource.RESOURCENAME.ToLower() == vmname.ToLower()))
But Except uses the Equals implementation which is case-sensitive.
If you don't want to use the comparer, that should do the trick too:
List<String> finallist = allcurrentresourcename.Where(r =>
!ScannedResourceNames.Contains(r.ToLower())
.ToList();

How to find duplicates in list and sort them by number of appearance

Is there a easy way to find duplicates in list, and then sort them by number of appearance? Also, duplicates should be removed.
Eg. you have a List<String> like this:
List<String> = new List<String>{"6","1","2","2","4","6","5","1","6","6","2"};
Question is, how to convert this list into -> "6", "1", "2", "4", "5"?
use Linq.Distinct()
List<String> list = new List<String>{"6","1","2","2","4","6","5","1","6","6","2"};
list = list.Distinct().ToList();
If you actually want them ordered from most common to least - unlike your example -
var ordered = list
.GroupBy(i => i)
.OrderByDescending(g => g.Count())
.Select(g => g.Key);
should achieve this.
I think the simplest way is to use LINQ method Distinct():
var originalList = …;
vat withoutDuplicates = originalList.Distinct();
Though you should note that the order of the result of Distinct() is explicitly left undocumented.
Using System.Linq;
// Assuming inputList was the original string list
List<String> deDuped = inputList.Distinct().ToList();
Not quite the cleanest yet, but this provides the correct result.
var originalData = new List<string>{"6","1","2","2","4","6","5","1","6","6","2"};
var result = new List<string>();
foreach (var myString in originalData)
{
if (!result.Exists(s => s == myString))
result.Add(myString);
}
Since it's unclear exactly what you mean, here's a solution for two possible meanings:
Get a unique list ordered by the number of occurrences:
Group the list and sort by Count():
List<String> list = new List<String>{"6","1","2","2","4","6","5","1","6","6","2"};
var q = from s in list
group s by s into g
orderby g.Count() descending
select g.Key;
Get a unique list ordered by the FIRST occurrence of the item in the list
There are already several suggestions to use Distinct, however the documentation DOES NOT GUARANTEE that they appear in the order they appear in the list (the current Enumerable implementation happens to order them that way, but for other providers it's not guaranteed):
Remarks
The result sequence is unordered.
To guarantee order, add an OrderBy clause:
var q = list.Distinct().OrderBy(s => list.IndexOf(s));

Remove elements from one List<T> that are found in another

I have two lists
List<T> list1 = new List<T>();
List<T> list2 = new List<T>();
I want remove all elements from list1, which also exist in list2. Of course I can loop through the first loop looking for each element in list2, but I am looking for elegant solution.
Thanks!
To change the actual list1 in place, you could use
list1.RemoveAll(item => list2.Contains(item));
You might instead prefer to simply have a query over the lists without modifying either
var result = list1.Except(list2);
LukeH makes a good recommendation in the comments. In the first version, and if list2 is particularly large, it might be worth it to load the list into a HashSet<T> prior to the RemoveAll invocation. If the list is small, don't worry about it. If you are unsure, test both ways and then you will know.
var theSet = new HashSet<YourType>(list2);
list1.RemoveAll(item => theSet.Contains(item));
With LINQ:
var result = list1.Except(list2);
list1.RemoveAll( item => list2.Contains(item));
Description
I think you mean the generic type List<Type>. You can use Linq to do this
Sample
List<string> l = new List<string>();
List<string> l2 = new List<string>();
l.Add("one");
l.Add("two");
l.Add("three");
l2.Add("one");
l2.Add("two");
l2.Add("three");
l2.Add("four");
l2.RemoveAll(x => l.Contains(x));
More Information
MSDN - List.RemoveAll Method
var result = list1.Except(list2);
Using LINQ you can do this:
List1.RemoveAll(i => !List2.Contains(i));
If you want to remove a list of objects (list2) from another list (list1) use:
list1 = list1.Except(list2).ToList()
Remember to use ToList() to convert IEnumerable<T> to List<T>.
var NewList = FirstList.Where(a => SecondList.Exists(b => b.ID != a.ID));
Using LINQ

How to remove identical items from List<string>?

I have a List in which I select users from db each time a sql query runs with certain value and selects one user in the time thus I cannot limit identical users in sql.
I have list with:
list[0] = "jerry"
list[1] = "tom"
list[2] = "jerry"
I want any (first or last doesn't matter in my case) to be removed from the list.
Thanks
IEnumerable<string> uniqueUsers = list.Distinct();
You can also use a HashSet:
HashSet<string> uniqueUsers = new HashSet<string>(list);
LINQ can solve this:
List<string> names = new List<string> { "Tom", "Jerry", "Tom" };
IQueryable<string> distinctItems = names.Distinct();
If you want a list type, simply call ToList():
distinctItems.ToList();
Here's an example from the MSDN.
EDIT: Non-LINQ Example (using Contains() from the List class):
List<string> names = new List<string> { "Tom", "Jerry", "Tom" };
List<string> distinctNames = new List<string>();
foreach (var name in names)
{
if (!distinctNames.Contains(name))
{
distinctNames.Add(name);
}
}
You can use the Distinct() LINQ extension.
var list = new List<string> { "Tom", "Jerry", "Tom" };
var uniqueList = list.Distinct();
Using Distinct, as suggested in the other answers, will leave your original list intact and return a separate IEnumerable<> sequence containing the distinct items from your list.
An alternative would be to remove duplicates from your original list directly, using RemoveAll:
var temp = new HashSet<string>();
yourList.RemoveAll(x => !temp.Add(x));
you can use list.distinct();

Tell LINQ Distinct which item to return

I understand how to do a Distinct() on a IEnumerable and that I have to create an IEqualityComparer for more advanced stuff however is there a way in which you can tell which duplicated item to return?
For example say you have a List<T>
List<MyClass> test = new List<MyClass>();
test.Add(new MyClass {ID = 1, InnerID = 4});
test.Add(new MyClass {ID = 2, InnerID = 4});
test.Add(new MyClass {ID = 3, InnerID = 14});
test.Add(new MyClass {ID = 4, InnerID = 14});
You then do:
var distinctItems = test.Distinct(new DistinctItemComparer());
class DistinctItemComparer : IEqualityComparer<MyClass> {
public bool Equals(MyClass x, MyClass y) {
return x.InnerID == y.InnerID;;
}
public int GetHashCode(MyClassobj) {
return obj.InnerID.GetHasCode();
}
}
This code will return the classes with ID 1 and 3. Is there a way to return the ID matches 2 & 4.
I don't believe it's actually guaranteed, but I'd be very surprised to see the behaviour of Distinct change from returning items in the order they occur in the source sequence.
So, if you want particular items, you should order your source sequence that way. For example:
items.OrderByDescending(x => x.Id)
.Distinct(new DistinctItemComparer());
Note that one alternative to using Distinct with a custom comparer is to use DistinctBy from MoreLINQ:
items.OrderByDescending(x => x.Id)
.DistinctBy(x => x.InnerId);
Although you can't guarantee that the normal LINQ to Objects ordering from Distinct won't change, I'd be happy to add a guarantee to MoreLINQ :) (It's the only ordering that is sensible anyway, to be honest.)
Yet another alternative would be to use GroupBy instead - then for each inner ID you can get all the matching items, and go from there.
You don't want distinct then - you want to group your items and select the "maximum" element for them, based on ID:
var distinctItems = test.Distinct(new DistinctItemComparer());
var otherItems = test.GroupBy(a => a.InnerID, (innerID, values) => values.OrderBy(b => b.ID).Last());
var l1 = distinctItems.ToList();
var l2 = otherItems.ToList();
l1 = your current list
l2 = your desired list
This doesn't sound like a job for Distinct, this sounds like a job for Where. You want to filter the sequence in your case:
var ids = new[] { 2, 4 };
var newSeq = test.Where(m => ids.Contains(m.ID));
If you want to select one particular of the group of elements that are considered equal using the comparison you use, then you can use group by:
var q = from t in tests
group t by t.InnerID into g
select g.First(...);
In the select clause, you'll get a collection of elements that are equal and you can select the one specific element you need (e.g. using First(...)). You actually don't need to add Distinct to the end, because you're already selecting only a single element for each of the groups.
No, there's no way.
Distinct() is used to find distinct elements. If you're worried about which element to return...then obviously they are not truly identical (and therefore not distinct) and you have a flaw in your design.

Categories

Resources