Remove items from list 1 not in list 2 - c#

I am learning to write lambda expressions, and I need help on how to remove all elements from a list which are not in another list.
var list = new List<int> {1, 2, 2, 4, 5};
var list2 = new List<int> { 4, 5 };
// Remove all list items not in List2
// new List Should contain {4,5}
// The lambda expression is the Predicate.
list.RemoveAll(item => item. /*solution expression here*/ );
// Display results.
foreach (int i in list)
{
Console.WriteLine(i);
}

You can do this via RemoveAll using Contains:
list.RemoveAll( item => !list2.Contains(item));
Alternatively, if you just want the intersection, using Enumerable.Intersect would be more efficient:
list = list.Intersect(list2).ToList();
The difference is, in the latter case, you will not get duplicate entries. For example, if list2 contained 2, in the first case, you'd get {2,2,4,5}, in the second, you'd get {2,4,5}.

Solution for objects (maybe easier than horaces solution):
If your list contains objects, rather than scalars, it is that simple, by removing by one selected property of the objects:
var a = allActivePatientContracts.RemoveAll(x => !allPatients.Select(y => y.Id).Contains(x.PatientId));

list = list.Except(list2).ToList();

This question has been marked as answered, but there is a catch. If your list contains an object, rather than a scalar, you need to do a bit more work.
I tried this over and over with Remove() and RemoveAt() and all sorts of things and none of them worked correctly. I couldn't even get a Contains() to work correctly. Never matched anything. I was stumped until I got the suspicion that maybe it could not match up the item correctly.
When I realized this, I refactored the item class to implement IEquatable, and then it started working.
Here is my solution:
class GenericLookupE : IEquatable<GenericLookupE>
{
public string ID { get; set; }
public bool Equals( GenericLookupE other )
{
if ( this.ID == other.ID ) return true;
return false;
}
}
After I did this, the above RemoveAll() answer by Reed Copsey worked perfectly for me.
See: http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx

Related

.net core list removeall() method is strange

It seems very simple, but this error happens.
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.ToList().RemoveAll(s => s == "002");
return list.Count.ToString();
The list Count should be 3, but it will still be 4. Is a bug in RemoveAll() method? If using List rathan than IList declaration, it works well.
Edit
1. If not using ToList() method, there is no RemoveAll() method to call.
How can I avoid this situation to use IList as a argument, list firstly is a reference type. Should I not use IList as a declaration totally? We have used IList everythere in our project.
public string List()
{
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
Remove(list);
return list.Count.ToString();
}
private void Remove(IList<string> list)
{
list.ToList().RemoveAll(a => a == "002");
}
If there is a new method to support, it will be better. Thanks everyone here.
When you're using ToList you actually create a shallow copy of the list, which then you apply the RemoveAll on the new list, thus, the original list doesn't been affected.
If you only looking for the count of none-"002" items, then simple count will suffice
list.Count - list.Count(i => i == "002");
Otherwise if you actually want to remove those items from the original list, then you will need to solve it the old fashion way, using for loop.
Anyway, if IList is not that important, you can save the list as List and not IList, and use RemoveAll method.
ToList() returns a shallow copy of the original list (ie, it's a new instance and modifications made to it will not be reflected back in list).
Since you need to use the RemoveAll method, I recommend storing the variable as List instead of IList:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.RemoveAll(s => s == "002");
Console.WriteLine(list.Count()); // 3
You can also achieve the same thing using LINQ operators:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list = list.Where(s => s != "002").ToList();
Console.WriteLine(list.Count());
More succinctly:
List<string> list = new List<string>(new[] {
"001",
"002",
"003",
"004"
});
// or
//List<string> list = new[] {
// "001",
// "002",
// "003",
// "004"
//}.ToList();
Console.WriteLine(list.Count(s => s != "002")); // 3

Decipher LINQ to Objects query involving strings

(Disclaimer: I am new to LINQ, and I did do my homework, but all my research came up with examples that deal with strings directly. My code deals with an ObservableCollection of objects where each object contains a string.)
I have the following code:
public class Word
{
public string _word { get; set; }
// Other properties.
}
ObservableCollection<Word> items = new ObservableCollection<Word>();
items.Add(new Word() { _word = "Alfa" });
items.Add(new Word() { _word = "Bravo" });
items.Add(new Word() { _word = "Charlie" });
I am trying to find the words that have an "r" in them:
IEnumerable<Word> subset = from ii in items select ii;
subset = subset.Where(p => p._word.Contains("r"));
The above works (I get 'Bravo' and 'Charlie').
My question: I have devised the above LINQ query from bits and pieces of examples I found online/in books.
How does it do what it does?
Is there be a better/more straightforward way?
Thanks.
You could achieve that you want more simple like below:
IEnumerable<Word> subset = items.Where(x => x._word.Contains("r"));
With the above linq query, you filter the collection called items using an extension method called Where. Each element in the collection of items that satisfies the filter in the Where method, it will be contained on the subset. Inside the Where you have defined your fitler, using a lambda expression. On the left side of => you have your input -the random element of items, while on the right side of => you have your output, which in your case will be either true or false. On the rigth side of =>, you usethe string's method called Contains, which in simple terms checks if the string you pass as a parameter to this method is contained in the property called _word, of the element x.

SQL Query -- Generate a 2 collections based on differences?

I have 2 different lists:
public class Foo
{
public Int32 Id { get; set; }
}
ICollection<Foo> OriginalCollection
ICollection<Foo> NewCollection
I want to generate 2 lists to be able to foreach them...
All items that OriginalCollection contains that NewCollection does not (basically, a collection of stuff that was removed).
All items that OriginalCollection does not contain that NewCollection does (basically, a collection of stuff that was added).
I know I'll get back an IEnumerable with LINQ, that's fine since I need to foreach. I just have no idea what my queries should look like...
Update:
I forgot to mention that I did try the Except clause... it failed because the objects are not the same. They only contain the same Id.
So, you want the Complement of the Set.
This post seems to fit your needs:
Quickest way to find the complement of two collections in C#
Or perhaps the Enumerable.Except method:
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx
You are looking for the Except operator
http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#except1
NewCollection -except> OriginalCollection = deleted
OriginalCollection -except> NewCollection = added
List<Foo> original = ...
List<Foo> modified = ...
...
var deleted = original.Except(modified);
var added = modified.Except(original);
You could explicitly use LINQ but its not really worth it. One way could be:
var deleted = from i in original
where !modified.Contains(i)
select i;
var added = from i in modified
where !original.Contains(i)
select i;
using System.Linq;
originalC.Except(newC, comparer);
newC.Except(originalC, comparer);
Following edit:
You can use the overload that takes a "IEqualityComparer comparer".
You can define a small class that implements that interface with any kind of compare logic you wish.

How would I remove items from a List<T>?

I have a list of items.
The problem is the returned items (which I have no control over) return the same items THREE time.
So while the actual things that should be in the list are:
A
B
C
I get
A
B
C
A
B
C
A
B
C
How can I cleanly and easily remove the duplicates? Maybe count the items, divide by three and delete anything from X to list.Count?
The quickest, simplest thing to do is to not remove the items but run a distinct query
var distinctItems = list.Distinct();
If it's a must that you have a list, you can always append .ToList() to the call. If it's a must that you continue to work with the same list, then you'd just have to iterate over it and keep track of what you already have and remove any duplicates.
Edit: "But I'm working with a class"
If you have a list of a given class, to use Distinct you need to either (a) override Equals and GetHashCode inside your class so that appropriate equality comparisons can be made. If you do not have access to the source code (or simply don't want to override these methods for whatever reason), then you can (b) provide an IEqualityComparer<YourClass> implementation as an argument to the Distinct method. This will also allow you to specify the Equals and GetHashCode implementations without having to modify the source of the actual class.
public class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject a, MyObject b)
{
// code to determine equality, usually based on one or more properties
}
public int GetHashCode(MyObject a)
{
// code to generate hash code, usually based on a property
}
}
// ...
var distinctItems = myList.Distinct(new MyObjectComparer());
if you are 100% sure that you receive everything you need 3 times, then just
var newList = oldList.Take(oldList.Count / 3).ToList()
Linq has a Distinct() method which does exactly this. Or put the items in a HashSet if you want to avoid duplicated completely.
If you're using C# 3 or up:
var newList = dupList.Distinct().ToList();
If not then sort the list and do the following:
var lastItem = null;
foreach( var item in dupList )
{
if( item != lastItem )
{
newItems.Add(item);
}
lastItem = item;
}
you could simply create a new list and add items to it that are not already there.

Union two List in C#

I want to union, merge in a List that contains both references, so this is my code, how can I define a list ready for this porpouses?
if (e.CommandName == "AddtoSelected")
{
List<DetalleCita> lstAux = new List<DetalleCita>();
foreach (GridViewRow row in this.dgvEstudios.Rows)
{
var GridData = GetValues(row);
var GridData2 = GetValues(row);
IList AftList2 = GridData2.Values.Where(r => r != null).ToList();
AftList2.Cast<DetalleCita>();
chkEstudio = dgvEstudios.Rows[index].FindControl("ChkAsignar") as CheckBox;
if (chkEstudio.Checked)
{
IList AftList = GridData.Values.Where(r => r != null).ToList();
lstAux.Add(
new DetalleCita
{
codigoclase = Convert.ToInt32(AftList[0]),
nombreestudio = AftList[1].ToString(),
precioestudio = Convert.ToDouble(AftList[2]),
horacita = dt,
codigoestudio = AftList[4].ToString()
});
}
index++;
//this line to merge
lstAux.ToList().AddRange(AftList2);
}
dgvEstudios.DataSource = lstAux;
dgvEstudios.DataBind();
}
this is inside a rowcommand event.
If you want to add all entries from AftList2 to lstAux you should define AftList2 as IEnumerable<> with elements of type DetalleCita (being IEnumerable<DetalleCita> is enough to be used as parameter of AddRange() on List<DetalleCita>). For example like this:
var AftList2 = GridData2.Values.Where(r => r != null).Cast<DetalleCita>();
And then you can add all its elements to lstAux:
lstAux.AddRange(AftList2);
Clarification:
I think you are misunderstanding what extension method ToList() does. It creates new list from IEnumerable<T> and its result is not connected with original IEnumerable<T> that it is applied to.
That is why you are just do nothing useful trying to do list.ToList().AddRange(...) - you are copying list to (another newly created by ToList()) list, update it and then basically throwing away it (because you are not even doing something like list2 = var1.ToList(), original var1 stays unchanged after that!!! you most likely want to save result of ToList() if you are calling it).
Also you don't usually need to convert one list to another list, ToList() is useful when you need list (List<T>) but have IEnumerable<T> (that is not indexable and you may need fast access by index, or lazy evaluates but you need all results calculated at this time -- both situations may arise while trying to use result of LINQ to objects query for example: IEnumerable<int> ints = from i in anotherInts where i > 20 select i; -- even if anotherInts was List<int> result of query ints cannot be cast to List<int> because it is not list but implementation of IEnumerable<int>. In this case you could use ToList() to get list anyway: List<int> ints = (from i in anotherInts where i > 20 select i).ToList();).
UPDATE:
If you really mean union semantics (e.g. for { 1, 2 } and { 1, 3 } union would be something like { 1, 2, 3 }, with no duplication of equal elements from two collections) consider switching to HashSet<T> (it most likely available in your situation 'cause you are using C# 3.0 and I suppose yoou have recent .NET framework) or use Union() extension method instead of AddRange (I don't think this is better than first solution and be careful because it works more like ToList() -- a.Union(b) return new collection and does NOT updates either a or b).

Categories