Remove elements from one list that are found in another - c#

This should be quite easy, however I am failing to see why all my methods are not working.
I have looked at all the solutions and used them appropriately however am not getting the result.
solutions include
Solution 1
Solution 2
Here is the code:
IEnumerable<feature> available = _repo.GetAvailableFeatures();
IEnumerable<feature> selected = _repo.GetSelectedFeatures();
Using Except
var filteredList = (available.Except(selected)).ToList;
Using Linq
var availableList = available.ToList();
var selectedList = selected.ToList();
availableList.RemoveAll(item => selectedList.Contains(item));
Using old fashion for
for (var i = 0; i < availableList.Count - 1; i++)
{
foreach (var t in selectedList)
{
if (availableList[i].Id == t.Id)
{
availableList.RemoveAt(i);
}
}
}
My Feature class looks like this:
public class Feature
{
public int Id;
public int Desc;
}
Can anyone see it my mistakes here?

When you use Except you need to define what "equal" means for the feature type, otherwise reference equality (are they the same object) is used by default. In your loop you define "equal" as "Ids are equal", so some options are:
Override Equals and GetHashCode in the feature class
This becomes the "default" definition of equal for the type
Define a class that implements IEqualityComparer<feature>
This could be used only when that definition is needed
Use Where instead of Except:
var filteredList = available.Where(a => !selected.Any(s => s.Id == a.Id))
.ToList();
Performance is sub-optimal but it is a simpler code solution if performance overall is not affected significantly.

This looks like a straightforward process of comparing the two data lists to each other and removing them in a standard fashion. I would personally go with a List setup instead of IEnumerable.
List<Feature> availableList = _repo.GetAvailableFeatures();
List<Feature> selectedList = _repo.GetSelectedFeatures();
foreach(Feature avail in availableList){
if(selectedList.Contains(avail)){
selectedList.Remove(avail)
}

Related

Compare two struct List and find if item of list1 appears in item of list2 in C#

I have two record structures and two lists as follows:
public struct gtAliasRecType : ICloneable
{
public int lRecordNum;
public double dLocationCd;
}
public struct gtCVARecType : ICloneable
{
public double dLocationCd;
}
static public List<gtCVARecType> LCVARec = null;
static public List<gtAliasRecType> LAliasRec = null;
Now i want to iterate "LAliasRec" list and find whether similar "dLocationCd" exists in "LCVARec" list or not.
I tried using "Contains" and "Find" function of list1 but ended up in errors.
public static void XYZ()
{
gtAliasRecType uAliasRec = gtAliasRecType.CreateInstance();
gtCVARecType uCVARec = gtCVARecType.CreateInstance();
for (int i = 0; i < LAliasRec.Count; i++)
{
uAliasRec = LAliasRec[i];
//trying Find method
gtCVARecType c1 = LCVARec.Find(uAliasRec.dLocationCd);
//trying Contains method
bool nReturn = LCVARec.Contains( uAliasRec.dLocationCd );
}
}
However, i ran into "Cannot convert from 'double' to 'gtCVARecType' error.
Contains & Find
Thanks in advance :)
You can't use Contains to find an item of a different type. You can use Find, but I'd personally use the LINQ Any method:
foreach (var uAliasRec in LAliasRec)
{
bool nReturn = LCVARec.Any(rec => rec.dLocationCd == uAliasRec.dLocationCd);
// Presumably do something with nReturn
}
If the lists are large, you might want to create a HashSet<double> for all the locations first, which is an up-front cost that will make everything else cheaper:
HashSet<double> locations = new HashSet<double>(LCVARec.Select(rec => rec.dLocationCd));
foreach (var uAliasRec in LAliasRec)
{
bool nReturn = locations.Contains(uAliasRec.dLocationCd);
// Presumably do something with nReturn
}
As an aside, I'd strongly advise you to start following regular .NET naming conventions. In its current form, your code is going to be very hard for anyone used to regular C# code to work with.
What about using Intersect
var results = LAliasRec
.Select(x => x.dLocationCd)
.Intersect(LCVARec.Select(x => x.dLocationCd));
bool exists = results.Count() > 0;
Select only the double values, and get intersected ones. If Count greater than 0, you got mutual property values.
You can use LINQ and Inner join to find the intersection of two lists.
var query = from lcva in LCVARec
join lAlias in LAliasRec on lcva.dLocationCd equals lAlias.dLocationCd
select lcva;
Console.WriteLine(query.Count()); //prints number of matching items.
Update
If you can change the List<T> to SortedList<TKey, TValue> of SortedDictionary<TKey, TValue> it will help in quicker lookup.
If you prefer to use Contains() you must implement IEquatable<T> and if you want performance you have to Sort() which needs the class to have IComparable<T> and then do BinarySearch
Reference : https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netcore-3.1#remarks

Using Where and ForEach to modify specific elements in a list

If I have a list of objects that have the properties fruitName and numFruits and I want to pluralize the fruitName where numFruits is greater than 1, is it possible to do that in a single statement by chaining together Where and Foreach?
Something like:
fruitList.Where(fl => fl.numFruits > 1).ForEach(fl => fl.fruitName = fl.fruitName + "s");
I tried the above and it doesn't work. It complains that System.Collections.Generic.IEnumerable doesn't contain a definition for ForEach.
Typically you want to use foreach the language construct when possible. Eric Lippert has a blog post going into additional detail as to why.
Loops are good when you are doing modifications as it makes finding those modifications easier.
foreach (var fl in fruitList.Where(fl => fl.numFruits > 1))
{
fl.fruitName = fl.fruitName + "s";
}
Is more straightforward and accomplishes the same task.
If you really want a one-liner (it will be harder to maintain) and want to keep the original list intact but only modify some of the elements, you'll have to use a full anonymous function. If you need multiple statements (a block of code), you'll need to include the braces and statement-terminating semicolons like below:
fruitList.ForEach(fl => { fl.fruitName = fl.numFruits > 1 ? fl.fruitName + "s" : fl.fruitName; });
This works on the original list (no subset) and does basically the exact same thing a structured foreach would do.
There's a good blog post by Eric Lippert on why there is no “ForEach” sequence operator extension method, essentially the reason is:
The first reason is that doing so violates the functional programming
principles that all the other sequence operators are based upon.
Clearly the sole purpose of a call to this method is to cause side
effects. The purpose of an expression is to compute a value, not to
cause a side effect. The purpose of a statement is to cause a side
effect. The call site of this thing would look an awful lot like an
expression (though, admittedly, since the method is void-returning,
the expression could only be used in a “statement expression”
context.) It does not sit well with me to make the one and only
sequence operator that is only useful for its side effects.
If you wanted to do this in a single statement you could use a .Select()
var newFruitList = fruitList.Where(fl => fl.numFruits > 1).Select(fl => fl.fruitName + "s");
Like #Tim Schmelter suggested, you can use ToList() to convert to a list and then use the ForEach method on the result returned. Although the ToList() might return a shorter list based on the filter, the original objects themselves would be changed and your fruitList will remain unchanged.
fruitList.Where(fl => fl.numFruits > 1).ToList().ForEach(fl => fl.fruitName = fl.fruitName + "s");
// fruitList still has all elements
You can use the static Array.ForEach method to update the list.
Array.ForEach(fruitList.Where(fl => fl.numFruits > 1).ToArray(), x => { x.fruitName += "s"; });
Given that "append an s" doesn't actually give you the correct answer for many fruits, any approach that does that will give you an incorrect answer, no matter how well it does it.
Consider using a lookup table to map singlular to plurals (and vice versa) instead:
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
private static Dictionary<string, string> fruitLookup =
new Dictionary<string, string>
{
{"blueberry", "blueberries"},
{"peach", "peaches"},
{"apple", "apples"}
};
public static void Main()
{
var fruitList = new List<string> {"blueberry", "peach", "apple"};
// Here is your one-line conversion:
var plurals = fruitList.Select(f => fruitLookup[f]).ToList();
foreach (var p in plurals)
{
Console.WriteLine(p);
}
}
}

What is the correct way of ignoring arguments in LINQ?

I have the following code:
foreach (var b in userNames.Select(a => new User()))
{
...
}
This works quite well, since it gives me all "fresh" user objects, however Code Analysis complains that I shouldn't create unused locals, so my question is, is there a way of ignoring the arguments (similar to the "_" in Haskell).
PS: prehaps my example is not the best. I am sorry for this.
Thanks!
Update 1
I got the following code analysis error:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "a"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "b")]
_ is a perfectly valid variable name in C#. So writing
foreach(var b in userNames.Select(_ => new User()))
{
}
is perfectly valid code. It depends on your analysis rules whether it accepts such cases or not.
However, your code is indeed quite suspicious: you're mapping a collection of user names to a collection of users but you're not specifying a direct relation between the two: maybe you wanted to write something like this:
foreach(var b in userNames.Select(username => new User(username)))
To create a collection of objects of a given size, just use the length from the original collection.
var newColletion = Enumerable.Repeat(false, input.Length)
.Select(_ => new User());
but perhaps better would be your own helper method
static class MyEnumerable {
IEnumberable<T> Repeat<T>(Func<T> generator, int count) {
for (var i = 0; i < count; ++i) {
yield return generator();
}
}
}
and then use
var newCollection = MyEnumerable.Repeat(() => new User(), oldCollection.Length);
If quantity is your concern, and need linq, rewrite it as
foreach(var user in Enumerable.Repeat(()=>new User(),Usernames.Count).Select(x=>x()))
{
}
But, it may look ugly based on how you see it.
I'm sure there is a valid case where you would want to ignore arguments like this, but this doesn't seem like one of them. If you are creating N User objects for N userNames, surely you want to couple those together?
foreach (var b in userNames.Select(a => new { name = a, user = new User() }))
{
...
}
Then you won't have any unused arguments.
But the question remains why you aren't just doing this:
foreach (var name in userNames)
{
var user = new User();
// ...
}
As far as I can see, your use of .Select() makes no sense here.
Select performs a projection on the collection on which it is called, performing a specified operation on each element and returning the transformed results in another collection. If you do not need to perform any operation on the lambda element, you'd better simply create an array of User objects directly.
Answering your edit, as I said above, you can simply do this:
var NewColl = new User[userNames.Length];
As for initialization, you could have done this:
Enumerable.Repeat<User>(new User(), userNames.Length);

Removing objects from a bindinglist bound to a datagrid view

I'm trying to remove objects from a BindingList that is bound to a DataGridView by doing this...
private void RemoveItems(List<Payment> removeList)
{
for (int i = removeList.Count - 1; i >= 0; i--)
{
sortableBindingPaymentList.Remove(removeList[i]);
}
}
Trying to debug this myself i attempted the following, however remover always = -1 (meaning that no match was found) and I'm 110% sure that my list of Payment's in removeList contains a match in my sortableBindingList...
private void RemoveItems(List<Payment> removeList)
{
int remover;
for (int i = removeList.Count - 1; i >= 0; i--)
{
remover = sortableBindingPaymentList.IndexOf(removerList[i]);
sortableBindingPaymentList.RemoveAt(remover);
}
}
Any help is appreciated and thanks in advance!
I'm not sure I would go the IEquatable way.. depending on the ORM you're using that might bring you some trouble.
Do your entities have a primary key? you can try this instead:
private void RemoveItems(List<Payment> removeList)
{
removeList.ForEach(x => sortableBindingPaymentList.RemoveAll(s => s.Id == x.Id));
}
P.S: I strongly suggest you to start using LinQ for these kind of operations instead of for loops.
if removeList doesn't contain the same references (same objects) as the ones in sortableBindingPaymentList then the method will return -1, which i assume happens in your case.
It states here:
that the "Remove" method of a list:
"This method determines equality using the default equality comparer EqualityComparer.Default for T, the type of values in the list."
Payment class should implement IEquatable interface.
Eg:
public class Payment : IEquatable
{
public bool Equals(Payment paymentObj)
{
//is current instance equal to payment OBJ?
//if yes, then return true otherwise false
}
}
Now this call should work even if you have different instances of objects in list collections.
sortableBindingPaymentList.Remove(removeList[i]);
Please post complete post if you need further help.

Ideal c# reduction method for values of the same type, with bitwise approach?

Good day all,
I have a class and a property, and I have three instances of that class.
public class myclass {
public int myproperty;
}
...
myclass inst1, inst2, inst3;
...
Now at a certain point I need to compare those three property values, and verify that they be equal or not, to end up with the least amount of values.
So if I have
inst1.myproperty = 3;
inst2.myproperty = 5;
inst3.myproperty = 3;
after the magic_function_call, I should get 3 and 5.
And if I have
inst1.myproperty = 2;
inst2.myproperty = 2;
inst3.myproperty = 2;
after the magic_function_call, I should get 2.
Albeit this is trivial per se, and can be solved with as many IF checks as needed, I was wondering which is the fastest, or more efficient way to do it, especially in light of the fact that I might need to add another variable to the check in the future.
I am in fact wondering if there is a bitwise operation that can be performed that can solve this elegantly and quickly.
Alternatively, is there an array operation that can be used to achieve the same goal? I've tried looking for 'reduction' or 'compression' but those keywords don't seem to lead in the right direction.
Thanks in advance.
You can use the morelinq DistinctBy query operator if all of the instances belong to a collection:
List<MyClass> myList = new List<MyClass>();
.. populate list
List<MyClass> distinct = myList.DistinctBy(mc => mc.myproperty).ToList();
Looking at the question, you may want a list of just the property values (a list of ints), which you can achieve with the standard query operators:
List<int> distinct = myList.Select(mc => mc.myproperty).Distinct().ToList();
Note that you haven't defined a property, you've defined a public field. To define an auto property change:
public int myproperty;
to
public int myproperty { get; set; }
Note also that PascalCasing is recommended for property and class names.
Here's a quick function which doesn't require any extra libraries and avoids the setup costs and overheads associated with LINQ:
static int[] Reduce(IEnumerable<myclass> items)
{
HashSet<int> uniqueValues = new HashSet<int>();
foreach (var item in items)
{
uniqueValues.Add(item.myproperty);
}
return uniqueValues.ToArray();
}
Pass it a collection of your myclass instances and it will return an array of unique myproperty values.
Just anohter way to implement it .
var result = myList.GroupBy(p => p.myproperty).Select(p => p.First());

Categories

Resources