Difficulty in Removing Items From List? - c#

I have two lists. The first is of all students and the second is of selected students. I want if I one time select some student they will remove from the all-student list. Here is my code but it doesn't. Students won't get removed.
foreach (var li in ListSelectedStudents.ToList())
{
if (ListAllStudents.Contains(li))
{
ListAllStudents.Remove(li);
}
}

Contains will use equality to determine what is "equal", I am assuming here that your custom class hasn't provided a custom equality implementation, which means the default equatable will be provided for that type and it's just using reference equality. So even though you think two things are "equal", the Contains method doesn't and so doesn't step into the Remove call.
To get that particular code to behave what you need to do is provide an implementation of IEquatable<Student> on the Student class, as described in the remarks here.
In this instance, Contains isn't actually required as Remove will do the same checks. If there is nothing to remove, the Remove call will be transparent, effectively doing nothing.
As has been caught in the comments before I had chance to provide the information, Remove will also rely on IEquatable<Student> (docs) so you still need to provide an implementation, but it will make your code look a little cleaner:
foreach (var li in ListSelectedStudents.ToList())
{
ListAllStudents.Remove(li);
}
There may be various ways to do this without the need to implement the interface, but you won't be able to use your current code for it. I'll leave other answers to field those alternatives as it's Friday and my brain is not yet functioning properly.

have you tried using linq:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Contains(m));
if it does not work, it could be something wrong with the default comparison implemented in the object, and you could either fix the comparer, or do something like:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Any(n=>n.Id == m.Id)); // Assume the Id is the primary key of the object...

Try this:
ListSelectedStudents = ListSelectedStudents.Where(a => !ListSelectedStudents.Contains(a)).Select(a => a).ToList();

Related

ASP.Net XUnit check if list of objects cointains a specific object

I am trying to add unit tests to my project. Some of these tests are checking if a list of objects does of does not contain an object. For checking if a list contains an object i've tried Assert.Contains(MyList, ExpectedObject), but it still gives an error that says that the list does not contain this object. Even when i debug the test i can see that the object is correctly added to the list.
The same happens with Assert.DoesNotContain(MyList, ExpectedObject). When i remove an item from the list and do this check it does say that it not in the list. But when i no longer remove the item, it still says that it is no longer in the list. Even though it is still in the list.
When i try it with a test list:List<string>. and do the same operations of adding and removing items, and then checking if these items are in the list or not. It does work.
Maybe Assert.Contains does not work for lists of objects. But the compiler does not give any errors. And i've also already checked if the ExpectedObject is the same type as the objects in the list.
Is there maybe another way of checking if an object is or isn't in a list.
any help is appreciated.
In your test, is ExpectedObject the actual object in the list or an object with the same values? In C# two objects with the same property values are not actually equal. I suspect this is why your test is failing. Two strings with the same value are considered equal because the string object implements the Equals method (and some more), like #dorukerenaktas points out.
There's multiple ways to go about this. The easiest is by checking if an object with the expected property values is in the collection.
There's an overload of Assert.Contains that allows you to specify a predicate, for example:
Assert.Contains(MyList, item => item.Id == expectedId)
Another option is to override the Equals method on your object, like #dorukerenaktas explains, but I would only recommend that if it really makes sense for your class. I would definitely not do that just so you can use this in a test.
Generally in Java and C# implementation of contains methods for object lists compare them by equals method. If equals method if not specified for the object by default it will look for object unique id or memory address, it means even if there were 2 objects with all same fields can be different. Because they created separately and allocated different ram addresses. If you want to compare objects override equals method. This will allow contains method to compare objects with each other using your custom comparison method.
Example:
Let say you have a Person object with fields like id, name, mail etc. If you want objects to be equal if their id's are same you can use:
// They are same person if their id is same
public override bool Equals(Object obj)
{
Person personObj = obj as Person;
if (personObj == null)
return false;
else
return idNumber.Equals(personObj.idNumber);
}
I have faced a similar problem before and I used fluentAssertions to solve it.
So for example if you have a list of objects called myList, this snipped should do the trick.
myList.Should().Contain(expectedItem);
There is more info on collections here: https://fluentassertions.com/collections/
The other option is to loop through the list and check if any of the objects are equal to your object. When doing this, if you use the ShouldBeEquivalentTo() method you will not need to check equality of every property. This is very useful if your object is large or nested, since this method checks each property recursively.
actual.Should().BeEquivalentTo(expected);
There is more information on this here: https://fluentassertions.com/objectgraphs/
I like using fluentAssetions because it makes my tests and assertions more readable, and also provides very clear messages when errors do occurs or assertions fail, so you find the problem faster.

Manually removing items from a collection vs using Enumerable.Except C#

To remove objects from a List of custom objects using Except method requires you to implement a IEqualityComparer on the object. But is it bad just to remove the objects in a normal foreach loop?
I understand the concept of the IEqualityComparer and using Except but I couldn't get it to work for some reason so I just removed the items manually. Is this considered bad programming?
EDIT: using the manual way id have to override Equals and GetHashcode polluting my view model - I guess that's bad?
In general, one should avoid making changes to a collection while enumerating.
I'm not entirely sure what your original problem is, or why you need to remove elements in such a way, but you're over-complicating the problem. If I understand it right, and you are in fact using a List<T> where T is a custom type. If this is the case, then simply use a LINQ query to get the values you want from the list.
var newList = oldList.Where(x => x.PropertyName != unwantedValue);
You could use Enumerable.Except, but it should be noted that Enumerable.Except returns a set, which is to say, no duplicate values are allowed.
Also, it should be noted that overriding .Equals and .GetHashCode does not pollute the viewmodel, as far as I know.
Sources:
http://msdn.microsoft.com/en-us/library/vstudio/bb336390(v=vs.100).aspx
Enumerable.Except Problem

How to check if a value exists in a list?

I'm relatively new to C# programming (programming as a whole, actually), but I've built an application to manage the application pools on a server that my team at work uses. It does everything it's supposed to fairly well, but the only issue I'm running into is in saving previously-used configurations to the app.config file so the user doesn't have to put them in manually every time. As it stands, I can save to and load from the file magnificently (along with all of the strings I need in each group).
The issue is that I want to do a cursory check to see if a Name string exists in the group before writing it. Example of the part of the app.config:
<appSettings>
<add Name="RowName" MachineName="MS-02348" AppSrvName="AppServer" WebSrvName="AppNet"/>
<add Name="RowName2" MachineName="MS-68186" AppSrvName="AppServer2" WebSrvName="AppNet2"/>
</appSettings>
So what I'm currently doing to load the values is I have a method that retrieves the appSettings/add nodes and throws them into a list, then sets the values to properties of an object. The reason I do this is so that I can have a drop-down that lists only the Name of an object, and then the rest of the information is all available for when I call the method on the selected item.
Anyway, what I'm running into now is that I want to make sure that if the Name already exists in the app.config, I prompt the user to write another name instead of saving it to the database. Having two child nodes with the same "Name" value would wreak havoc on my logic.
I tried a foreach to cycle through the objects in the list, but without knowing how many objects there could be I didn't know of an easy way of really saying it does or does not exist. I've also tried targeting the childnode based on the values listed in the node, but it seems to fail there too. I'm guessing that part is syntax, but it seems to match up with how the method list defines it.
Any thoughts?
if (list.Any())
{
// found something!
}
else
{
// found nothing
}
I always use Any() simply because it's the most performant. List.Count() goes through each item and counts them, but you don't care about the number of items -- you only care if there's an item at all. Any() will enumerate through the list and stop if it finds an item. So, in the extreme case, in a list of a million items Count() will enumerate every single one and return while Any() will enumerate one and return.
Plus, it returns a bool, which is handy for more concise code. :)
As an added bonus, you can call Any() looking for specific things. So, in a list of people I can look to see if there are any people older than 21 in it:
if (list.Any(person => person.Age > 21))
{
// ...
}
Edit: Formatting.
maybe something like this
var list = new List<AppSettings>();
var item = list.FirstOrDefault(x => x.Name == NameEnteredByUser);
if (item == null)
{
//there is no such item
}
else
{
//notify the user
}
or the Any extension method:
var list = new List<AppSettings>();
if (list.Any(x => x.Name == NameEnteredByUser))
{
//name exists
}
else
{
//no such name used before
}
As a sidenote, have a unique field configured in your database so that when your programming logic fails you wont enter a record. corrupt data state in db is bad.
neo112 is correct in his logic, but I am unsure if the main problem you have is performance related, since you mention you dont know if it may get too long.
First, you could also do the following;
int count = list.Count(a => a.Name == NameEnteredByUser);
if(count > 0)
{
// exists
}
I believe .Count() is faster than .First() (anecdotal evidence only) and personally think it's a bit cleaner.
Also, another thing you could try to do is to sort your list by name when adding to the appSettings node. Then, you should instantiate a SortedList instead of just List, then that would also (possitively) affect performance. But, I am unsure if sorting is an option for you.

InvalidOperationException while sorting an ObservableCollection

In my program I have TreeView nodes that I need to be able to shift up and down, basically change the order. My TreeView is an ObservableCollection of a specific Data Model. Each node has a property called "Rank", this is the value that I would like to sort the collection by. With that being said I referred to this question. From that question I discovered this blog page. I am attempting the second method involving the sort function of a List.
This is the example that I am looking at:
List<Person> list = new List<Person>(people);
list.Sort();
Please note that the "Rank" value of each node is in working order and changing correctly. I just need to find a way to re-order the collection based off of that property and reflect it in the View.
My Problem: When trying to implement the above solution I get an InvalidOperationException. I feel like I do not understand how to tell the List to sort based off of rank.
What my code looks like:
List<TreeModel> sortedTree = new List<TreeModel>(TreeCollection);
sortedTree.Sort();
What am I missing here? How do I sort the collection based off of the rank property and reflect those changes in the view?
Thank you.
*I believe I may have posted about this before, so if for some reason this question is too similar my older one, I'll just delete the old one.
Sort throws InvalidOperationException its component type does not have a default comparison:
The default comparer Comparer.Default cannot find an implementation of the IComparable generic interface or the IComparable interface for type T.
You can however supply the comparison as the first parameter to Sort:
sortedTree.Sort((x, y) => x.Rank.CompareTo(y.Rank));
To pass the sorted items back to the original collection, you could either clear/repopulate CurrentCollection, or simply assign it a new instance (don't forget to RaisePropertyChanged if you do the latter):
CurrentCollection = new ObservableCollection<TreeModel>(sortedTree);
You need to pass property name on which you want to sort your list like this -
sortedTree = sortedTree.OrderBy(m => m.Rank).ToList();

Common problem for me in C#, is my solution good, stupid, reasonable? (Advanced Beginner)

Ok, understand that I come from Cold Fusion so I tend to think of things in a CF sort of way, and C# and CF are as different as can be in general approach.
So the problem is: I want to pull a "table" (thats how I think of it) of data from a SQL database via LINQ and then I want to do some computations on it in memory. This "table" contains 6 or 7 values of a couple different types.
Right now, my solution is that I do the LINQ query using a Generic List of a custom Type. So my example is the RelevanceTable. I pull some data out that I want to do some evaluation of the data, which first start with .Contains. It appears that .Contains wants to act on the whole list or nothing. So I can use it if I have List<string>, but if I have List<ReferenceTableEntry> where ReferenceTableEntry is my custom type, I would need to override the IEquatable and tell the compiler what exactly "Equals" means.
While this doesn't seem unreasonable, it does seem like a long way to go for a simple problem so I have this sneaking suspicion that my approach is flawed from the get go.
If I want to use LINQ and .Contains, is overriding the Interface the only way? It seems like if there way just a way to say which field to operate on. Is there another collection type besides LIST that maybe has this ability. I have started using List a lot for this and while I have looked and looked, a see some other but not necessarily superior approaches.
I'm not looking for some fine point of performance or compactness or readability, just wondering if I am using a Phillips head screwdriver in a Hex screw. If my approach is a "decent" one, but not the best of course I'd like to know a better, but just knowing that its in the ballpark would give me little "Yeah! I'm not stupid!" and I would finish at least what I am doing completely before switch to another method.
Hope I explained that well enough. Thanks for you help.
What exactly is it you want to do with the table? It isn't clear. However, the standard LINQ (-to-Objects) methods will be available on any typed collection (including List<T>), allowing any range of Where, First, Any, All, etc.
So: what is you are trying to do? If you had the table, what value(s) do you want?
As a guess (based on the Contains stuff) - do you just want:
bool x= table.Any(x=>x.Foo == foo); // or someObj.Foo
?
There are overloads for some of the methods in the List class that takes a delegate (optionally in the form of a lambda expression), that you can use to specify what field to look for.
For example, to look for the item where the Id property is 42:
ReferenceTableEntry found = theList.Find(r => r.Id == 42);
The found variable will have a reference to the first item that matches, or null if no item matched.
There are also some LINQ extensions that takes a delegate or an expression. This will do the same as the Find method:
ReferenceTableEntry found = theList.FirstOrDefault(r => r.Id == 42);
Ok, so if I'm reading this correctly you want to use the contains method. When using this with collections of objects (such as ReferenceTableEntry) you need to be careful because what you're saying is you're checking to see if the collection contains an object that IS the same as the object you're comparing against.
If you use the .Find() or .FindAll() method you can specify the criteria that you want to match on using an anonymous method.
So for example if you want to find all ReferenceTableEntry records in your list that have an Id greater than 1 you could do something like this
List<ReferenceTableEntry> listToSearch = //populate list here
var matches = listToSearch.FindAll(x => x.Id > 1);
matches will be a list of ReferenceTableEntry records that have an ID greater than 1.
having said all that, it's not completely clear that this is what you're trying to do.
Here is the LINQ query involved that creates the object I am talking about, and the problem line is:
.Where (searchWord => queryTerms.Contains(searchWord.Word))
List<queryTerm> queryTerms = MakeQueryTermList();
public static List<RelevanceTableEntry> CreateRelevanceTable(List<queryTerm> queryTerms)
{
SearchDataContext myContext = new SearchDataContext();
var productRelevance = (from pwords in myContext.SearchWordOccuranceProducts
where (myContext.SearchUniqueWords
.Where (searchWord => queryTerms.Contains(searchWord.Word))
.Select (searchWord => searchWord.Id)).Contains(pwords.WordId)
orderby pwords.WordId
select new {pwords.WordId, pwords.Weight, pwords.Position, pwords.ProductId});
}
This query returns a list of WordId's that match the submitted search string (when it was List and it was just the word, that works fine, because as an answerer mentioned before, they were the same type of objects). My custom type here is queryTerms, a List that contains WordId, ProductId, Position, and Weight. From there I go about calculating the relevance by doing various operations on the created object. Sum "Weight" by product, use position matches to bump up Weights, etc. My point for keeping this separate was that the rules for doing those operations will change, but the basic factors involved will not. I would have even rather it be MORE separate (I'm still learning, I don't want to get fancy) but the rules for local and interpreted LINQ queries seems to trip me up when I do.
Since CF has supported queries of queries forever, that's how I tend to lean. Pull the data you need from the db, then do your operations (which includes queries with Aggregate functions) on the in-memory table.
I hope that makes it more clear.

Categories

Resources