I have a HashSet. Is there a method that can utilize the IEqualityComparer for retrieving items where you pass in an object that will satisfies the equals method defined in the IEqualityComparer?
This might explain it a bit more.
public class Program
{
public static void Main()
{
HashSet<Class1> set = new HashSet<Class1>(new Class1Comparer());
set.Add( new Class1() { MyProperty1PK = 1, MyProperty2 = 1});
set.Add( new Class1() { MyProperty1PK = 2, MyProperty2 = 2});
if (set.Contains(new Class1() { MyProperty1PK = 1 }))
Console.WriteLine("Contains the object");
//is there a better way of doing this, using the comparer?
// it clearly needs to use the comparer to determine if it's in the hash set.
Class1 variable = set.Where(e => e.MyProperty1PK == 1).FirstOrDefault();
if(variable != null)
Console.WriteLine("Contains the object");
}
}
class Class1
{
public int MyProperty1PK { get; set; }
public int MyProperty2 { get; set; }
}
class Class1Comparer : IEqualityComparer<Class1>
{
public bool Equals(Class1 x, Class1 y)
{
return x.MyProperty1PK == y.MyProperty1PK;
}
public int GetHashCode(Class1 obj)
{
return obj.MyProperty1PK;
}
}
If you want to retrieve items based on a single property, you might want to use a Dictionary<T,U> instead of a hashset. You can then place the items within the dictionary, using MyProperty1PK as the key.
Your query then becomes simple:
Class1 variable;
if (!dictionary.TryGetValue(1, out variable)
{
// class wasn't in dictionary
}
Given that you're already storing using a comparer which only uses this value as the uniqueness criteria, there is really no disadvantage to just using that property as the key in a dictionary instead.
Related
I would like to know how can with a SortedSet, I can Sort by one property of my object and in the other hand set the unicity to another property.
Here what I have :
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var bonusSortedSet = new SortedSet<Bonus>(new ComparerByNumber());
var bonus0 = new Bonus()
{
ID = "6479cc32-960d-4aa0-a62d-8c81e65085e8",
Number = 15
};
var bonus1 = new Bonus()
{
ID = "8e8a9c1b-1889-4c4c-b039-b1dbe005719b",
Number = 10
};
var bonus2 = new Bonus()
{
ID = "3453f78d-ce28-4ab1-a7a1-395063374f87",
Number = 11
};
var bonus3 = new Bonus()
{
ID = "8e8a9c1b-1889-4c4c-b039-b1dbe005719b",
Number = 12
};
bonusSortedSet.Add(bonus0);
bonusSortedSet.Add(bonus1);
bonusSortedSet.Add(bonus2);
bonusSortedSet.Add(bonus3);
foreach (var bonus in bonusSortedSet)
{
Console.WriteLine($"{bonus.ID} : {bonus.Number}");
}
}
}
public class Bonus : IEqualityComparer<Bonus>
{
public string ID { get; set; }
public int Number { get; set; }
public bool Equals(Bonus x, Bonus y)
{
return x.GetHashCode() == y.GetHashCode();
}
public int GetHashCode(Bonus obj)
{
return obj != null ? obj.ID.GetHashCode() : string.Empty.GetHashCode();
}
}
public class ComparerByNumber : IComparer<Bonus>
{
public int Compare(Bonus x, Bonus y)
{
return Math.Sign(x.Number - y.Number);
}
}
}
The result is :
8e8a9c1b-1889-4c4c-b039-b1dbe005719b : 10
3453f78d-ce28-4ab1-a7a1-395063374f87 : 11
8e8a9c1b-1889-4c4c-b039-b1dbe005719b : 12
6479cc32-960d-4aa0-a62d-8c81e65085e8 : 15
I would have expect :
8e8a9c1b-1889-4c4c-b039-b1dbe005719b : 10
3453f78d-ce28-4ab1-a7a1-395063374f87 : 11
6479cc32-960d-4aa0-a62d-8c81e65085e8 : 15
You haven't implemented any code to make the Bonus objects unique by their ID property, only by their Number property. Even if you did, such as:
public int Compare(Bonus x, Bonus y)
{
if (x.ID == y.ID) return 0;
return Math.Sign(x.Number - y.Number);
}
This can work in some situations, but is completely driven by the internal implementation of SortedSet<T> and is unlikely to work 99% of the time.
You can't have the set sorted by one property and the uniqueness dictated by a different one. If you want to track order and uniqueness on two independent properties, you'll need two collection objects.
Your class implements IEqualityComparer<Bonus> which is wrong. You do not want a bonus to be a comparer, you want a bonus to be equatable.
Also, do not implement Equals by checking if hash codes are identical. Hash collisions will happen (but will be infrequent with a good GetHashCode).
Try writing the Bonus type like this:
public sealed class Bonus
{
public string ID { get; set; }
public int Number { get; set; }
public override bool Equals(object obj)
{
if (obj is Bonus other)
{
return ID == other.ID;
}
return false;
}
public override int GetHashCode()
{
return ID?.GetHashCode() ?? 0;
}
}
Then a HashSet<> with the implicit (default) equality comparer:
var bonusSet = new HashSet<Bonus>();
will ensure the set members are unique by ID. Then you can use Linq to sort the members when you enumerate them:
foreach (var bonus in bonusSet.OrderBy(x => x.Number))
{
Console.WriteLine($"{bonus.ID} : {bonus.Number}");
}
I would like to add classStudents to the list _ClassStudents only if classStudents is not yet in the list.
public class ClassStudents{
public Id {get; set;}
public Name {get; set;}
public List<Student> Student {get; set;}
}
public static List<ClassStudents> _ClassStudentsList = new List<ClassStudents>();
public static void addClassStudents(ClassStudents classStudents)
{
//if( classStudents isn't in ClassStudentsList ) <------
_ClassStudentsList.Add(classStudents);
}
How can I do this?
You can use the Any extension method:
var text = nameClassStudents.Text;
if(!_ClassStudentsList.Any(t => t.Text == text))
{
_ClassStudentsList.Add(new ClassStudents(text,(int)numericUpDown1.Value));
}
However, this does not guarantee that names will be unique in the list.
If you want it guaranteed, you can simply use a Dictionary<string, int> instead of a List<ClassStudents>.
The Dictionary will not allow duplicate keys.
Of course, here you would also want to check if the key exists before adding a value to the dictionary:
var dict = new Dictionary<string, int>;
...
var text = nameClassStudents.Text;
if(!dict.ContainsKey(text))
(
dict.Add(text, (int)numericUpDown1.Value);
)
You can also use a HashSet:
public class ClassStudents {
public Id {get; set;}
public Name {get; set;}
public override bool Equals(object obj) {
return this.Name.Trim().ToLower().Equals(((ClassStudents)obj).Name.Trim().ToLower());
}
public override int GetHashCode() {
return this.Name.GetHashCode();
}
}
In your main(), you can declare a HashSet like below:
HashSet <ClassStudents> hash = new HashSet<ClassStudents>();
Now, this will add only unique elements in the set.
I think the proper way is to tell your program what it means for two Turma types to be equal.
public class ClassStudents
{
public string Name { get; set; }
public int Value { get; set; }
// Override the equality operation to check for text value only
public override bool Equals(object obj)
{
if (obj is ClassStudents other) // use pattern matching
{
return Name.Equals(other.Name);
}
return false;
}
}
Now you can use the list method .Contains() for checking if item exists.
{
List<ClassStudents> classStudents = new List<ClassStudents>();
public Test()
{
// Add three values
classStudents.Add(new ClassStudents() { Name="ABC", Value=101 });
classStudents.Add(new ClassStudents() { Name="IJK", Value=111 });
classStudents.Add(new ClassStudents() { Name="XYZ", Value=101 });
// Check if exists before adding this value
ClassStudents x = new ClassStudents() { Name="ABC", Value=121 };
// `Contains()` calls the `Equals()` method of `classStudents`
if (!classStudents.Contains(x))
{
classStudents.Add(x);
}
}
}
This results in the cleanest code because it is obvious what the check if(!classStudents.Contains(x)) does. Also you are leveraging the power of the CLR by adding intelligence to your types. OOP is all about adding methods in your user classes to define actions. In this case, I am definition what equality means for this type and now the CLR can use this information where needed.
I have written the following code to implement Linq.Distinct(IEqualityComparer) in the most basic way possible, however simpleCollection returns 2 items instead if 1.
Oddly, ive noticed that breakpoints on Equals never get hit.
Could it be something to do with my implementation of GetHashCode()?
public class testobjx
{
public int i { get; set; }
}
public class mytest
{
public Main()
{
var simpleCollection = new[] { new testobjx() { i = 1 }, new testobjx() { i = 1 } }.Distinct(new DistinctCodeType());
var itemCount = simpleCollection.Count();//this should return 1 not 2.
}
}
public class DistinctCodeType : IEqualityComparer<testobjx>
{
public bool Equals(testobjx x, testobjx y)
{
return x.i == y.i;
}
public int GetHashCode(testobjx obj)
{
return obj.GetHashCode();
}
}
Try:
public int GetHashCode(testobjx obj)
{
if (obj == null) return 0;
return obj.i.GetHashCode();
}
The default implementation of GetHashCode for an object is based on the object's instance, so two instances of testobjx with the same value have different hash codes. You need to modify your GetHashCode method to interrogate the object's property. If the object has multiple properties you need to figure out which ones are required to uniquely identify the object and compose a single hash code from those.
I have set of properties as follows:
public string Foo1 {set;get;}
public string Foo2 {set;get;}
public string Foo3 {set;get;}
public string Foo4 {set;get;}
public string Foo5 {set;get;}
public string Foo6 {set;get;}
public string Foo7 {set;get;}
public string Foo8 {set;get;}
public string Foo9 {set;get;}
......
public string Foo50 {set;get;}
then i m iterating through a collection as follows:
foreach(var element in sortedCollection.Keys){
if(element != null)
// in this block I would like to assign the element to the properties above
// ex:
foo?? = sortedCollection[element];
// ?? need to be replaced by index.
}
Is there an easy way to do this?
I think a better design would be:
public List<string> Foos { get; private set; }
If you can't change it, you could probably do something like:
var type = typeof(MyCalss);
int index = 1;
foreach (var key in sortedCollection.Keys)
{
var value = sortedCollection[key];
var prop = type.GetProperty("Foo" + index);
if (prop != null) prop.SetValue(this, value, null);
index++;
}
... of course with some error handling, and where this assumes this is a method within your class. Can you determine an index based on the values in your sortedCollection?
you can use reflection and do it in a loop:
for ( int i = 1 ; i < 51 ; i++)
this.GetType().GetProperty(string.Format("Foo{0}",i)).SetValue(this,desiredValue,null);
but i think i'll recommend using indexers
http://msdn.microsoft.com/en-us/library/2549tw02%28v=vs.80%29.aspx
You can do what you want by:
Using a for loop instead of foreach. This way you can operate with the current index.
Using reflections. You can get a list of properties for your class and access them dynamically. For example, see Type.GetProperties.
But why don't you just use a List<string> Foos instead of lots of properties?
You should use reflection.
this.GetType().GetProperty("Foo" + i).SetValue(this, sortedCollection[element], null);
Two things though:
GetProperty's cost is not null. So if you're doing this a lot of times, you might want to store the result of GetProperty in some field and then use this field in your foreach.
If your properties are really named Something1, Something2, etc..., then you may have a design flaw you may want to correct before doing this (replace all you string members with one List).
You need to use reflection (Type.GetProperty()) to get the property and set it's value.
Assuming that the properties are defined in class called MyClass:
foreach(var element in sortedCollection.Keys){
if(element != null)
// in this block I would like to assign the element to the properties above
// ex:
//foo?? = sortedCollection[element];
// not sure how you are getting the index here, may be you need to use for loop
PropertyInfo pi = typeof(MyClass).GetProperty("Foo" + index);
// ?? need to be replaced by index.
if (pi != null)
{
pi.SetValue(<object of MyClass>, sortedCollection[element], null);
}
}
void Main()
{
var foo = new Foo();
foo[1] = "Foo1";
//foo.Dump();
}
public class Foo
{
public string Foo1 {set;get;}
public string Foo2 {set;get;}
public string Foo3 {set;get;}
public string Foo4 {set;get;}
public string Foo5 {set;get;}
public string Foo6 {set;get;}
public string Foo7 {set;get;}
public string Foo8 {set;get;}
public string Foo9 {set;get;}
public string this[int index]
{
get
{
return getPropertyValue(index);
}
set
{
setPropertyValue(index, value);
}
}
private void setPropertyValue(int i, string value)
{
var propi = this.GetType().GetProperty("Foo" + i);
if (propi != null)
propi.SetValue(this,value,null);
}
private string getPropertyValue(int i)
{
var propi = this.GetType().GetProperty("Foo" + i);
if (propi != null)
return (string)propi.GetValue(this, null);
return null;
}
}
I would actually use reflection, or if this is called a lot, make a dynamic method and ILEmit to do it (much faster at runtime than reflection).
However just to suggest something different, you could change the class containing the Foo* properties to have each getter/setter read from an indexed list:
public class FooOfDoom
{
public string[] Foos = new string[2];
public string Foo1
{
set { Foos[0] = value; }
get { return Foos[0]; }
}
public string Foo2
{
set { Foos[1] = value; }
get { return Foos[1]; }
}
}
Then your class doesn't really change, as far as its contract with all the other code, since the properties are still there, but now you can assign right to Foos instead of through each individual property.
Again, in reality I would actually use a DynamicMethod if I was doing it myself.
Personally, I disagree with most of the other posters here. I think the use of reflection should be limited to those situations where it is really called for (object inspection, certain GUI situations, etc). In this case, with just a little more typing, it is possible to write a strongly-typed program and still do what you want. I'll offer two alternatives. Both alternatives will offer the ability to access your properties by name as well as by index.
In the first alternative, I'll assume that we are allowed to change the definition of your properties. In the second alternative, I'll assume that those definitions must remain unchanged.
The first alternative moves the data to a separate array, adds helper methods to access the data by index, and alters the properties to use the helper methods:
private class Version1 {
private readonly string[] underlyingData=new string[50];
public string Foo1 { get { return ReadFoo(1); } set { SetFoo(1, value); } }
public string Foo2 { get { return ReadFoo(2); } set { SetFoo(2, value); } }
public string Foo3 { get { return ReadFoo(3); } set { SetFoo(3, value); } }
//......
public string Foo50 { get { return ReadFoo(50); } set { SetFoo(50, value); } }
private string ReadFoo(int index) {
return underlyingData[index-1]; //1-based indexing
}
private void SetFoo(int index, string value) {
underlyingData[index-1]=value; //1-based indexing
}
}
The second alternative leaves the property definitions unchanged, and two static arrays of delegates representing the reading and writing function of those properties.
private class Version2 {
private static readonly Func<Version2, string>[] readers=new Func<Version2, string>[] {
c => c.Foo1,
c => c.Foo2,
c => c.Foo3,
//......
c => c.Foo50,
};
private static readonly Action<Version2, string>[] writers=new Action<Version2, string>[] {
(c,v) => c.Foo1=v,
(c,v) => c.Foo2=v,
(c,v) => c.Foo3=v,
//......
(c,v) => c.Foo50=v,
};
public string Foo1 { set; get; }
public string Foo2 { set; get; }
public string Foo3 { set; get; }
//......
public string Foo50 { set; get; }
private string ReadFoo(int index) {
return readers[index-1](this); //1-based indexing
}
private void SetFoo(int index, string value) {
writers[index-1](this, value); //1-based indexing
}
}
I have a class with 2 strings and 1 double (amount).
class Donator
string name
string comment
double amount
Now I have a Array of Donators filled.
How I can sort by Amount?
If you implement IComparable<Donator> You can do it like this:
public class Donator :IComparable<Donator>
{
public string name { get; set; }
public string comment { get; set; }
public double amount { get; set; }
public int CompareTo(Donator other)
{
return amount.CompareTo(other.amount);
}
}
You can then call sort on whatever you want, say:
var donors = new List<Donator>();
//add donors
donors.Sort();
The .Sort() calls the CompareTo() method you implemented for sorting.
There's also the lambda alternative without IComparable<T>:
var donors = new List<Donator>();
//add donors
donors.Sort((a, b) => a.amount.CompareTo(b.amount));
You can also use delegates:
class Program
{
static void Main(string[] args)
{
List<Donor> myDonors = new List<Donor>();
// add stuff to your myDonors list...
myDonors.Sort(delegate(Donor x, Donor y) { return x.amount.CompareTo(y.amount); });
}
}
class Donor
{
public string name;
public string comment;
public double amount;
}
By implementing IComparable and then use Array.Sort.
public class Donator : IComparable {
public string name;
public string comment;
public double amount;
public int CompareTo(object obj) {
// throws invalid cast exception if not of type Donator
Donator otherDonator = (Donator) obj;
return this.amount.CompareTo(otherDonator.amount);
}
}
Donator[] donators; // this is your array
Array.Sort(donators); // after this donators is sorted
I always use the list generic, for example
List<Donator> MyList;
then I call MyList.Sort
MyList.Sort(delegate (Donator a, Donator b) {
if (a.Amount < b.Amount) return -1;
else if (a.Amount > b.Amount) return 1;
else return 0; );
You could use MyArray.OrderBy(n => n.Amount)
providing you have included the System.Linq namespace.
Here is a sort without having to implement an Interface. This is using a Generic List
List<Donator> list = new List<Donator>();
Donator don = new Donator("first", "works", 98.0);
list.Add(don);
don = new Donator("first", "works", 100.0);
list.Add(don);
don = new Donator("middle", "Yay", 101.1);
list.Add(don);
don = new Donator("last", "Last one", 99.9);
list.Add(don);
list.Sort(delegate(Donator d1, Donator d2){ return d1.amount.CompareTo(d2.amount); });
Another way is to create a class that implements IComparer, then there is an overload to pass in the Comparer class.
http://msdn.microsoft.com/en-us/library/8ehhxeaf.aspx
This way you could have different classes for each specific sort needed. You could create one to sort by name, amount, or others.