Remove an item from a custom list [duplicate] - c#

This question already has answers here:
C# Remove object from list of objects
(11 answers)
Closed 6 years ago.
I have a List<Fill> items
Here is the Fill class :
public class Fill
{
public string arret { get; set;}
public string ligneSens { get; set;}
}
How can I remove an item from this List using the string value arret and ligneSens ?
Example :
void OnDelete(object sender, EventArgs e, string arretName, string ligneSensName)
{
var aFill = new Fill
{
arret = arretName,
ligneSens = ligneSensName
};
items.Remove(aFill); //<--- this doesn't work
}
Sorry for the title of the post I didn't know how to ask that.

You should override the Equals and GetHashCode methods of object. When you call the Remove on a reference type (classes) then it will look for one by it's Equals method.
public class Fill
{
public string arret { get; set; }
public string ligneSens { get; set; }
public override bool Equals(object obj)
{
var other = obj as Fill;
if(other == null)
return false;
return other.arret == arret && other.ligneSens == ligneSens;
}
public override int GetHashCode()
{
return arret.GetHashCode() ^
ligneSens.GetHashcode();
}
}
For what are the best practices on GetHashCode() overriding search online, there are many questions about it
Another way, is to specifically say what to remove by passing a predicate to evaluate for each item in the collection:
items.RemoveAll(item => item.arret = arretName &&
item.ligneSens == ligneSensName);

You have to use RemoveAll() for that, currently you are passing a newly created object, about which items does not have information, because it is a new reference not the same reference :
void OnDelete(object sender, EventArgs e, string arretName, string ligneSensName)
{
items.RemoveAll(item => item.arret == arretName && item.ligneSens == ligneSensName);
}

Use List API RemoveAll
items.RemoveAll(x => x.arret.Equals("value") && x.ligneSensName.Equals("ligneValue"));

Related

Check all properties in List

I have List of class as :-
public class Requirement
{
public string Id { get; set; }
public string desc { get; set; }
}
List lstRequirement
I have 3 records in this list for Id and desc.
I wanted to check if any of item is not remaining null.
For that I used below :-
bool IsHavingValidTags = lstRequirement.All(_=> _.Id!=null && _.desc!=null);
This condition is working fine with above Linq.
But I wanted to make it as Generic.
Eg. In future there may get added 5 more properties in Requirement class.
After addition of properties I also have to make changes in Linq.
How can I make this Linq condition generic for all properties?
I want to check any of the property is not remaining null in List.
Please help..!!!
I tried With =>
bool IsHavingValidTags = lstRequirement.All(_ => _ != null);
But not giving desired result.
EDIT 1 :
You can write an extension method that uses reflection like the following:
public static class Extensions
{
public static bool AreAllPropertiesNotNullForAllItems<T>(this IEnumerable<T> items)
{
var properties = typeof(T).GetProperties();
return items.All(x => properties.All(p => p.GetValue(x) != null));
}
}
then use like this:
bool IsHavingValidTags = lstRequirement.AreAllPropertiesNotNullForAllItems();
EDIT:
PropertyInfo.GetValue(object obj) method overload was introduced in .NET Framework 4.5. If you are using .NET Framework 4.0 you need to call p.GetValue(x, null)
Instead of this you should make those field not null. this will never allow those field inserted null. keep validations. like bellow.
[Required(ErrorMessage = "First name is required")]
public string first_name { get; set; }
[Required(ErrorMessage = "Last name is required")]
public string last_name { get; set; }
You can use foreach loop to loop through all the object in the list. Then use reflection to get all the properties in each item in the list, then you can loop through those properties to perform your null check.
Foreach (var x in lstRequirement){
List prop = x.GetType().GetProperties();
Foreach (var y in prop){
If (y == null){
IsHavingValidTag = true;
//Then you can return you method here or throw an Exception
}
}
Hope this helps.
You should add an static method to check the Properties of the Class. I will show you the following example.
Instead of your code :
bool IsHavingValidTags = lstRequirement.All(_ => _ != null);
use the following codes:
bool flg = list.All(m => CheckProperties(m));
public static bool CheckProperties<T>(T source)
{
bool rtnFlg = true;
Type t = typeof(T);
var properties = t.GetProperties();
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value == null)
{
return false;
}
}
return rtnFlg;
}

How to manage multiple classes in C#? (Duplication) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Simple question. I am writing an application in VS 2010 C# for fun (Learning experience) and I don't know how to get around a problem in an acceptable way.
Lets say I have a class called "Biscuit"
class Biscuit
{
private string Brand = String.Empty;
private string Variant = String.Empty;
public void CreateBiscuit(string pBrand, string pVariant)
{
Brand = pBrand;
Variant = pVariant;
}
public void GetDetails(ref string pBrand, ref string pVariant)
{
pBrand = Brand;
pVariant = Variant;
}
}
The user can create as many instances of this object as they want. All they have to do is give a brand and variant. I then create the object and store it in a list
Problem is, I don't want there to be duplicates. So no two objects where both the Brand and Variant are the same as another instance .
I could loop through all objects in the BiscuitLibrary, but this doesn't seem like good practice, or good performance. (Current Code Below)
List<Biscuit> BiscuitLibrary = new List<Biscuit>();
public void CheckDuplicate(string pBrand, string pVariant)
{
string ObjectBrand = String.Empty
string ObjectVariant = String.Empty;
foreach (Biscuit CurrentItem in BiscuitLibrary)
{
CurrentItem.GetDetails(ref ObjectBrand, ref ObjectVariant);
if ((ObjectBrand == pBrand) && (ObjectVariant == pVariant))
{
MessageBox.Show("This Biscuit Already Exists, Sorry!");
}
else
{
Biscuit NewBiscuit = new Biscuit();
NewBiscuit.CreateBiscuit(pBrand, pVariant);
BiscuitLibrary.Add(NewBiscuit);
}
}
}
How should I do this?
Thanks, Tom!
Probably the best practice would be to store all instances in a HashSet instead of a List. This would be especially useful for performance if you planned to store thousands of instances. Going through each of them in a List and checking for duplicates would be an expensive operation. While HashSet organizes its contents based on object hash codes in a way that allows very efficient duplicate checks. You can read more about HashSet here:
http://msdn.microsoft.com/en-us/library/bb397727(v=vs.110).aspx
Before HashSet can be used, you have to prepare your class by overriding its GetHashCode() and Equals() methods:
class Biscuit
{
public string Brand { get; set; }
public string Variant { get; set; }
public Biscuit(string brand, string variant)
{
Brand = brand;
Variant = variant;
}
public override bool Equals(object obj)
{
if (obj == null || typeof(Biscuit) != obj.GetType())
return false;
Biscuit biscuitObj = obj as Biscuit;
return biscuitObj.Brand == this.Brand && biscuitObj.Variant == this.Variant;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + this.Brand.GetHashCode();
hash = hash * 23 + this.Variant.GetHashCode();
return hash;
}
}
}
Now you can use this class with a HashSet:
//we create a HashSet and add 4 biscuits to it
//one of the biscuits is duplicate and will not be inserted
var uniqueBiscuitBox = new HashSet<Bisquit>();
uniqueBiscuitBox.Add(new Biscuit("A", "1"));
uniqueBiscuitBox.Add(new Biscuit("A", "1"));
uniqueBiscuitBox.Add(new Biscuit("B", "1"));
uniqueBiscuitBox.Add(new Biscuit("C", "4"));
Console.WriteLine(uniqueBiscuitBox.Count()); //returns 3
As you already using something similar to a Factory pattern you could do some modifications of your code tom make it work as you want to:
class Biscuit
{
// You should use properties
public string Brand {get; private set;}
public string Variant {get; private set;}
// Use a protected constructor so it can't be used outside the class
protected Biscuit(string brand, string variant)
{
Brand = brand;
Variant = variant;
}
// Use a static list to hold the already crated biscuits
private static List<Biscuit> _existingBiscuits = new List<Biscuit>();
// "Factory method" to create Biscuit instances
public static Biscuit CreateBiscuit(string pBrand, string pVariant)
{
// Use LINQ to find if there are any biscuits already crated with the brand and variant
if (_existingBiscuits.Any(b => b.Brand == pBrand && b.Variant == pVariant))
{
// If the biscuit already exist return null, no biscuit created
return null;
}
else
{
// Create biscuit and add it to the list
var biscuit = new Biscuit(pBrand, pVariant);
_existingBiscuits.Add(biscuit);
return biscuit;
}
}
}
First suggestion: require instances be constructed with valid data; throw if the user tries to pass invalid data and avoid the “construct then initialize” antipattern.
public class Biscuit : IEquatable<Biscuit>
{
public string Brand { get; private set; }
public string Variant { get; private set; }
public Biscuit(string brand, string variant)
{
if (brand == null) throw new ArgumentNullException("brand");
if (variant == null) throw new ArgumentNullException("variant");
Brand = brand;
Variant = variant;
}
public bool Equals(Biscuit b)
{
if (b == null) return false;
return string.Equals(Brand, b.Brand, StringComparison.InvariantCulture)
&& string.Equals(Variant, b.Variant, StringComparison.InvariantCulture);
}
}
If you want a list with special rules (e.g. no two items have the same Brand and Variant) then create a class that implements the rules. Do not use generic lists and try to enforce rules at runtime.
Copying #SoftwareFactor's idea, you can create strongly-typed HashSets with an IEqualityComparer:
class BiscuitEqualityComparer : IEqualityComparer<Biscuit>
{
public bool Equals(Biscuit b1, Biscuit b2)
{
if (b1 == null) return b2 == null;
return b1.Equals(b2);
}
public int GetHashCode(Biscuit b)
{
if (b == null) throw new ArgumentNullException("b");
return b.Brand.GetHashCode() ^ b.Variant.GetHashCode();
}
}
var bh = new HashSet<Biscuit>(new BiscuitEqualityComparer());
bh.Add(new Biscuit("Alpha", "Beta"));
bh.Add(new Biscuit("Alpha", "Gamma"));
bh.Add(new Biscuit("Alpha", "Beta")); // will not be added.

Not able to remove a Generic List object in c#

SessionResponseList objClientSessionResponseList = new SessionResponseList();
objClientSessionResponseList.QId = Convert.ToInt32(Session["QuestionNumber"]);
objClientSessionResponseList.QAnswer = Session["CurrentAnswer"].ToString();
objSessionResponseList = (List<SessionResponseList>)Session["Answers"];
if (objSessionResponseList.Where(x=>x.QId == objClientSessionResponseList.QId && x.QAnswer==objClientSessionResponseList.QAnswer).Count()>0)
{
objSessionResponseList.Remove(objClientSessionResponseList);
Session["Answers"] = objSessionResponseList;
}
// objSessionResponseList.Remove(objClientSessionResponseList);
//This isn't working tried everything the values are exact duplicate
Please help.
public class SessionResponseList{
public int QId { get; set; }
public string QAnswer { get; set; }
}
Instead of creating a new instance you should try getting the instance from the List using FirrstOrDefault and if that is found then remove that instance from the list, currently you are creating a new object and you are trying to remove that from the list.
var itemToBeRemoved = objSessionResponseList
.FirstOrDefault(x=>
x.QId == Convert.ToInt32(Session["QuestionNumber"]) &&
x.QAnswer == Session["CurrentAnswer"].ToString();
if(itemToBeRemoved != null) //means item is found in the list
objSessionResponseList.Remove(itemToBeRemoved)

List.except on custom class

lets say I have a custom class:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
Lets say I now have a List of this custom class:
List<WineCellar> orignialwinecellar = List<WineCellar>();
containing these items:
2012 Chianti 12
2011 Chianti 6
2012 Chardonay 12
2011 Chardonay 6
I know that if I want to compare two list and return a new list that has only items that are not in the other list I would do:
var newlist = list1.Except(list2);
How can I extend this to a custom class? Lets say I have:
string[] exceptionwinelist = {"Chardonay", "Riesling"};
I would like this to be returned:
List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);
This pseudocode obviously doesnt work but hopefully illustrates what I m trying to do. This shoudl then return a List of the custom class winecellar with following items:
2012 Chianti 12
2011 Chianti 6
Thanks.
You don't really want to use Except here, as you don't have a collection of WineCellar objects to use as a blacklist. What you have is a collection of rules: "I don't want objects with such and such wine names".
Therefore it's better to simply use Where:
List<WineCellar> result = originalwinecellar
.Where(w => !exceptionwinelist.Contains(w.wine))
.ToList();
In human-readable form:
I want all WineCellars where the wine name is not present in the list of exceptions.
As an aside, the WineCellar class name is a bit misleading; those objects are not cellars, they are inventory items.
One solution is with an extension method:
public static class WineCellarExtensions
{
public static IEnumerable<WineCellar> Except(this List<WineCellar> cellar, IEnumerable<string> wines)
{
foreach (var wineCellar in cellar)
{
if (!wines.Contains(wineCellar.wine))
{
yield return wineCellar;
}
}
}
}
And then use it like this:
List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
exceptionWineList is a string[] but originalWineCellar is a List<WineCellar>, WineCellar is not a string, so it does not make sense to perform an Except between these.
You could just as easily do,
// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));
What I think you are alluding to in your question is something like
WineCellar : IEquatable<string>
{
...
public bool Equals(string other)
{
return other.Equals(this.wine, StringComparison.Ordinal);
}
}
which allows you to equate WineCellars to strings.
However, if I were to rework your model I'd come up with something like,
enum WineColour
{
Red,
White,
Rose
}
enum WineRegion
{
Bordeaux,
Rioja,
Alsace,
...
}
enum GrapeVariety
{
Cabernet Sauvignon,
Merlot,
Ugni Blanc,
Carmenere,
...
}
class Wine
{
public string Name { get; set; }
public string Vineyard { get; set; }
public WineColour Colour { get; set; }
public WineRegion Region { get; set; }
public GrapeVariety Variety { get; set; }
}
class WineBottle
{
public Wine Contents { get; set; }
public int Millilitres { get; set; }
public int? vintage { get; set; }
}
class Bin : WineBottle
{
int Number { get; set; }
int Quantity { get; set; }
}
class Cellar : ICollection<WineBottle>
{
...
}
Then, you can see that there are several ways to compare Wine and I may want to filter a Cellar on one or more of Wine's properties. Therefore I might be temtpted to give myself some flexibility,
class WineComparer : EqualityComparer<Wine>
{
[Flags]
public Enum WineComparison
{
Name = 1,
Vineyard= 2,
Colour = 4,
Region = 8,
Variety = 16,
All = 31
}
private readonly WineComparison comparison;
public WineComparer()
: this WineComparer(WineComparison.All)
{
}
public WineComparer(WineComparison comparison)
{
this.comparison = comparison;
}
public override bool Equals(Wine x, Wine y)
{
if ((this.comparison & WineComparison.Name) != 0
&& !x.Name.Equals(y.Name))
{
return false;
}
if ((this.comparison & WineComparison.Vineyard) != 0
&& !x.Vineyard.Equals(y.Vineyard))
{
return false;
}
if ((this.comparison & WineComparison.Region) != 0
&& !x.Region.Equals(y.Region))
{
return false;
}
if ((this.comparison & WineComparison.Colour) != 0
&& !x.Colour.Equals(y.Colour))
{
return false;
}
if ((this.comparison & WineComparison.Variety) != 0
&& !x.Variety.Equals(y.Variety))
{
return false;
}
return true;
}
public override bool GetHashCode(Wine obj)
{
var code = 0;
if ((this.comparison & WineComparison.Name) != 0)
{
code = obj.Name.GetHashCode();
}
if ((this.comparison & WineComparison.Vineyard) != 0)
{
code = (code * 17) + obj.Vineyard.GetHashCode();
}
if ((this.comparison & WineComparison.Region) != 0)
{
code = (code * 17) + obj.Region.GetHashCode();
}
if ((this.comparison & WineComparison.Colour) != 0)
{
code = (code * 17) + obj.Colour.GetHashCode();
}
if ((this.comparison & WineComparison.Variety) != 0)
{
code = (code * 17) + obj.Variety.GetHashCode();
}
return code;
}
}
this probably looks like a lot of effort but it has some use. Lets say we wanted all the wine except the Red Rioja in your cellar, you could do something like,
var comparison = new WineComparer(
WineComparison.Colour + WineComparison.Region);
var exception = new Wine { Colour = WineColour.Red, Region = WineRegion.Rioja };
var allButRedRioja = cellar.Where(c =>
!comparison.Equals(c.Wine, exception));
I had this exact same issue to. I tried the example from Darren but couldn't get that to work properly.
I therefore made a modification from Darren´s example as follows:
static class Helper
{
public static IEnumerable<Product> Except(this List<Product> x, List<Product> y)
{
foreach(var xi in x)
{
bool found = false;
foreach (var yi in y) { if(xi.Name == yi.Name) { found = true; } }
if(!found) { yield return xi; }
}
}
}
This works for me. You can possibly add several fields in the if clause if needed.
To directly use such extension methods with generic classes you should implement comparator. It consists of two methods: Equal and GetHashCode. You should implement them in your WineCellar class.
Note the second example.
Note that the hash-based methods are much faster than basic 'List.Contains...' implementations.

MSTest: CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of

Question:
Can anyone tell me why my unit test is failing with this error message?
CollectionAssert.AreEquivalent failed. The expected collection contains 1
occurrence(s) of . The actual
collection contains 0 occurrence(s).
Goal:
I'd like to check if two lists are identical. They are identical if both contain the same elements with the same property values. The order is irrelevant.
Code example:
This is the code which produces the error. list1 and list2 are identical, i.e. a copy-paste of each other.
[TestMethod]
public void TestListOfT()
{
var list1 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
var list2 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
}
public class MyPerson
{
public string Name { get; set; }
public int Age { get; set; }
}
I've also tried this line (source)
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
and this line (source)
CollectionAssert.AreEquivalent(list1.ToArray(), list2.ToArray());
P.S.
Related Stack Overflow questions:
I've seen both these questions, but the answers didn't help.
CollectionAssert use with generics?
Unit-testing IList with CollectionAssert
You are absolutely right. Unless you provide something like an IEqualityComparer<MyPerson> or implement MyPerson.Equals(), the two MyPerson objects will be compared with object.Equals, just like any other object. Since the objects are different, the Assert will fail.
It works if I add an IEqualityComparer<T> as described on MSDN and if I use Enumerable.SequenceEqual. Note however, that now the order of the elements is relevant.
In the unit test
//CollectionAssert.AreEquivalent(list1, list2); // Does not work
Assert.IsTrue(list1.SequenceEqual(list2, new MyPersonEqualityComparer())); // Works
IEqualityComparer
public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
if (object.ReferenceEquals(x, y)) return true;
if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name && x.Age == y.Age;
}
public int GetHashCode(MyPerson obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
int hasCodeAge = obj.Age.GetHashCode();
return hashCodeName ^ hasCodeAge;
}
}
I was getting this same error when testing a collection persisted by nHibernate. I was able to get this to work by overriding both the Equals and GetHashCode methods. If I didn't override both I still got the same error you mentioned:
CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of .
The actual collection contains 0 occurrence(s).
I had the following object:
public class EVProjectLedger
{
public virtual long Id { get; protected set; }
public virtual string ProjId { get; set; }
public virtual string Ledger { get; set; }
public virtual AccountRule AccountRule { get; set; }
public virtual int AccountLength { get; set; }
public virtual string AccountSubstrMethod { get; set; }
private Iesi.Collections.Generic.ISet<Contract> myContracts = new HashedSet<Contract>();
public virtual Iesi.Collections.Generic.ISet<Contract> Contracts
{
get { return myContracts; }
set { myContracts = value; }
}
public override bool Equals(object obj)
{
EVProjectLedger evProjectLedger = (EVProjectLedger)obj;
return ProjId == evProjectLedger.ProjId && Ledger == evProjectLedger.Ledger;
}
public override int GetHashCode()
{
return new { ProjId, Ledger }.GetHashCode();
}
}
Which I tested using the following:
using (ITransaction tx = session.BeginTransaction())
{
var evProject = session.Get<EVProject>("C0G");
CollectionAssert.AreEquivalent(TestData._evProjectLedgers.ToList(), evProject.EVProjectLedgers.ToList());
tx.Commit();
}
I'm using nHibernate which encourages overriding these methods anyways. The one drawback I can see is that my Equals method is based on the business key of the object and therefore tests equality using the business key and no other fields. You could override Equals however you want but beware of equality pollution mentioned in this post:
CollectionAssert.AreEquivalent failing... can't figure out why
If you would like to achieve this without having to write an equality comaparer, there is a unit testing library that you can use, called FluentAssertions,
https://fluentassertions.com/documentation/
It has many built in equality extension functions including ones for the Collections. You can install it through Nuget and its really easy to use.
Taking the example in the question above all you have to write in the end is
list1.Should().BeEquivalentTo(list2);
By default, the order matters in the two collections, however it can be changed as well.
I wrote this to test collections where the order is not important:
public static bool AreCollectionsEquivalent<T>(ICollection<T> collectionA, ICollection<T> collectionB, IEqualityComparer<T> comparer)
{
if (collectionA.Count != collectionB.Count)
return false;
foreach (var a in collectionA)
{
if (!collectionB.Any(b => comparer.Equals(a, b)))
return false;
}
return true;
}
Not as elegant as using SequenceEquals, but it works.
Of course to use it you simply do:
Assert.IsTrue(AreCollectionsEquivalent<MyType>(collectionA, collectionB, comparer));

Categories

Resources