I have a simple class called User :
public class User
{
public int ID { get; set; }
public int MI { get; set; }
public User(int id, int mi)
{
ID = ID;
MI = mi;
}
}
And later on, I have a HashSet of Users that I want to get the ID's from and assign to a in HashSet as follows :
HashSet<Users> _users = new HashSet<>();
//code where several User objects are assigned to _users
HashSet<int> _usersIDs = new HashSet<int>();
_usersIDs = _users.Select("ID")
But this doesn't work, how can I successfully assigned all of the int ID's in _users to a new HashSet?
You can do:
HashSet<int> _usersIDs = new HashSet<int>(_users.Select(user=> user.ID));
But you should override GetHashCode for your User class if you are going to use it in a HashSet<T> and possibily Eqauls as well like:
public class User
{
protected bool Equals(User other)
{
return ID == other.ID && MI == other.MI;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((User) obj);
}
public override int GetHashCode()
{
unchecked
{
return (ID*397) ^ MI;
}
}
public int ID { get; set; }
public int MI { get; set; }
public User(int id, int mi)
{
ID = id; //based on #Jonesy comment
MI = mi;
}
}
Related
I am receiving KeyNotFoundException whenever I try to access the key in dictionary.
I have a PricingRules class which has a dictionary:
public class PricingRules
{
Dictionary<ItemCode, Money> pricing_rules;
public PricingRules()
{
pricing_rules = new Dictionary<ItemCode, Money>();
pricing_rules.Add(new ItemCode("A"), new Money(50));
pricing_rules.Add(new ItemCode("B"), new Money(30));
pricing_rules.Add(new ItemCode("C"), new Money(20));
pricing_rules.Add(new ItemCode("D"), new Money(15));
}
public void itemScanned(ItemCode itemCode, Money amount)
{
amount.Add(pricing_rules[itemCode]);
}
public Money AmountForItem(ItemCode itemCode)
{
return pricing_rules[itemCode];
}
}
The key in the dictionary is of type ItemCode:
public class ItemCode
{
private readonly string itemCode;
public ItemCode(string itemCode)
{
this.itemCode = itemCode;
}
public override int GetHashCode()
{
return itemCode.GetHashCode();
}
}
and the value is of type Money:
public class Money
{
private int amount;
public Money(int amount)
{
this.amount = amount;
}
public override bool Equals(object obj)
{
Money money = (Money)obj;
return this.amount == money.amount;
}
public void Add(object obj)
{
Money money = (Money)obj;
this.amount += money.amount;
}
public override int GetHashCode()
{
return amount.GetHashCode();
}
}
I have tried overriding the GetHashCode() function to return the HashCode of the primitive type in the class but it still is giving KeyNotFoundException
The test I have written is:
public class PricingRulesShould
{
[Fact]
void ReturnValueFiftyWhenKeyIsA()
{
Money expected = new Money(50);
PricingRules pr = new PricingRules();
ItemCode itemcode = new ItemCode("A");
Money actual = pr.AmountForItem(itemcode);
Assert.Equal(expected, actual);
}
}
When Dictionary accesses its elements, it looks for the equality of keys.
Since the ItemCode is not a primitive type, when you compare new ItemCode("A") == new ItemCode("A") you should get false as a result, because they are different instances with identical contents.
What you can do is to implement IEquatable<ItemCode> by the ItemCode class:
public class ItemCode : IEquatable<ItemCode>
{
private readonly string itemCode;
public ItemCode(string itemCode)
{
this.itemCode = itemCode;
}
public override int GetHashCode()
{
return itemCode != null ? itemCode.GetHashCode() : 0;
}
public bool Equals(ItemCode other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(itemCode, other.itemCode);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is ItemCode ic && Equals(ic);
}
}
I have several classes with id property of the same type int?:
public class Person {
public int? id { get; set; }
}
public class Project {
public int? id { get; set; }
}
// etc...
When writing code it happened that I compared semantically wrong types:
if (person.id == project.id), and of course there was no warning until I found the bug.
How could I create some kind of underlying type enforcement, or even better, a compiler warning, or something like that, that warns me not everything looks o.k.?
I can think of creating an Equals(Person p) { return p.id == this.id } but I'd prefer some other mechanism that could be used more 'freely'.
You need to override Equals and GetHashCode to be able to compare objects directly.
Try like this:
public sealed class Person : IEquatable<Person>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Person(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Person)
return Equals((Person)obj);
return false;
}
public bool Equals(Person obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Person left, Person right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Person left, Person right)
{
return !(left == right);
}
}
public sealed class Project : IEquatable<Project>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Project(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Project)
return Equals((Project)obj);
return false;
}
public bool Equals(Project obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Project left, Project right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Project left, Project right)
{
return !(left == right);
}
}
I also implemented IEquatable<Person> and == and != for good measure.
Now you can write person1 == this if this is a Person, but you would have a compiler error if this were a Project.
This is what tests are for. This is why you should write tests. Tests should pick up on these kind of errors.
But if you really want to go overkill, create a custom struct to store your IDs:
public struct Id<T> {
public int? ID { get; }
public static implicit operator Id<T>(int id) {
return new Id<T>(id);
}
public Id(int? id) { ID = id; }
public static bool operator ==(Id<T> lhs, Id<T> rhs) {
return lhs.ID == rhs.ID;
}
public static bool operator !=(Id<T> lhs, Id<T> rhs) {
return lhs.ID != rhs.ID;
}
}
// usage:
public class Person {
public Id<Person> Id { get; set; }
}
public class Project {
public Id<Project> Id { get; set; }
}
Whenever you try to compare Person.Id with Project.Id, the compiler will give you an error because you are comparing Id<Project> and Id<Person>.
So I have two Lists.
List<Farmer> CSVFarmer;
List<Farmer> Farmers;
CSVFarmer List gets its items from a method that will read a csv file.
Farmers List gets its items from a table in a sql database;
Now what I want to do is compare the two lists and return a list of non matching items;
For example if List CSVFarmer has:
FarmerName ContractNumber ContactNumber
John 2468 12345
Mike 13579 15790
And List Farmers has:
FarmerName ContractNumber ContactNumber
Mike 13579 15790
The list being returned should only have one item in it : Farmer John.
Farmer Class:
public class Farmer:INotifyPropertyChanged
{
int _id;
string _firstName;
string _farmerNo;
string _contactNumber;
public Farmer()
{
_firstName = string.Empty;
_farmerNo = string.Empty;
_contactNumber = string.Empty;
}
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string FarmerNo
{
get { return _farmerNo; }
set
{
_farmerNo = value;
OnPropertyChanged("FarmerNo");
}
}
public string ContactNumber
{
get { return _contactNumber; }
set
{
_contactNumber = value;
OnPropertyChanged("ContactNumber");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged (string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
Ive tried this:
public class vmUserManagement
{
public List<Farmer> Farmer { get; set; }
public List<Farmer> CSVFarmers { get; set; }
public List<Farmer> Farmers { get; set; }
public vmUserManagement()
{
CSVFarmers = new List<Farmer>();
Farmers = new List<Farmer>();
Farmer = new List<Farmer>();
}
public List<Farmer> getAllFarmers()
{
Farmers = RepoDapper.getAllFarmers();
return Farmers;
}
public List<Farmer> CSVImportFarmer()
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.DefaultExt = ".csv";
openFile.Filter = "(.csv) | *.csv";
var browseFile = openFile.ShowDialog();
if (browseFile == true)
{
string FilePath = openFile.FileName;
List<Farmer> values = File.ReadAllLines(FilePath).Select(v => FromFarmerCsv(v)).ToList();
CSVFarmers = values;
}
return CSVFarmers;
}
public static Farmer FromFarmerCsv(string csvLine)
{
string[] values = csvLine.Split(',');
Farmer farmer = new Farmer();
farmer.FirstName = values[0];
farmer.FarmerNo = values[1];
farmer.ContactNumber = values[2];
return farmer;
}
public List<Farmer> validateFarmerList()
{
foreach (var a in CSVFarmers)
{
foreach (var b in Farmers)
{
if (a != b)
{
Farmer.Add(a);
}
}
}
return Farmer;
}
}
The problem I'm having is that I will end up with two entries in List Farmer. Both for Farmer John and Farmer Mike. When I should only be getting a List containing Farmer John. Why is that?
I've also tried using Except:
public List<Farmer> validateFarmerList()
{
Farmer = CSVFarmers.Except(Farmers).ToList();
return Farmer;
}
But I still get two items in my Farmer List (Farmer John and Mike) instead of one.
Am I missing something? Any help will be much appreciated.
You have't overridden Equals and GethashCode in your Farmer class. That's why (a != b) doesn't work and also Enumerable.Except fails for the same reason: only references are compared and both are different instances.
How should .NET know that the ContractNumber of the farmer is relevant to identify him? One wayy is to tell it by overriding Equals and GetHashCode:
public class Farmer : IEquatable<Farmer>
{
public string FarmerName { get; set; }
public string ContractNumber { get; set; }
// .... other properties etc
public bool Equals(Farmer other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(ContractNumber, other.ContractNumber);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Farmer) obj);
}
public override int GetHashCode()
{
return (ContractNumber != null ? ContractNumber.GetHashCode() : 0);
}
}
Now you can use Except:
Farmer = CSVFarmers.Except(Farmers).ToList();
2nd way is to implement a custom IEqualityComparer<Farmer>, f.e if you can't change the Farmer class itself or you don't want to change it's behaviour and just want a custom comparer:
public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}
You can use this comparer in many LINQ methods, for example also in Enumerable.Except:
Farmer = CSVFarmers.Except(Farmers, new FarmerContractComparer()).ToList();
This approach has the advantage that you could provide different comparers for different tasks.
3rd approach: use LINQ and don't create a new class(less reusable and efficient but less work):
Farmer = CSVFarmers.Where(f => !Farmers.Any(f2 => f.ContractNumber == f2.ContractNumber)).ToList();
You will need to either create a class implementing IEqualityComparer<Farmer> and pass an instance of that as a second parameter to .Except() or implement IEquatable<Farmer> on your Farmer class.
The other answer already has a good implementation of the latter. If you always want farmers to be equal when their contract number is equal, use that one.
So if you want your Except work on contract numbers, but in other places you want other criteria, you need the first option:
class FarmerEqualWhenContractEqualComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the farmers' contracts are equal.
return x.ContractNumber == y.ContractNumber;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Farmer farmer)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
return farmer.ContractNumber?.GetHashCode() ?? 0;
}
}
Then you can do:
var changedOrNew = CSVFarmers.Except(Farmers, new FarmerEqualWhenContractEqualComparer()).ToList();
i have this certain Key in my Dictionary:
public class KKey
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
problem was everytime I generate new Guid, this condition wouldnt work:
if (false == myDictionary.TryGetValue(key, out keyobj))
because Guid key was new ...
Now my question was, how can i make condition that verify if Kkey.Name was already added, then dont add?
You either need to create a custom comparer or have your class override Equals and GetHashCode
Option 1: Comparer
sealed class NameEqualityComparer : IEqualityComparer<KKey>
{
public bool Equals(KKey x, KKey y)
{
return string.Equals(x.Name, y.Name);
}
public int GetHashCode(KKey obj)
{
return (obj.Name != null ? obj.Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>(new NameEqualityComparer());
Option 2: Override
public class KKey : IEquatable<KKey>
{
public string Name { get; set; }
public Guid Guid { get; set; }
public bool Equals(KKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
return Equals(obj as KKey);
}
public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
can also be
bool alreadyAdded = keywordList.Any(n => n.Key.Name == name);
if (!alreadyAdded) { }
here is the object code:
public class DlpItem : IEqualityComparer<DlpItem>
{
public string Text { get; set; }
public int Id { get; set; }
public DlpItem(int pId)
{
Text = string.Empty;
Id = pId;
}
public override bool Equals(object obj)
{
return Id == (obj as DlpItem).Id;
}
public bool Equals(DlpItem a, DlpItem b)
{
return a.Id == b.Id;
}
public int GetHashCode(DlpItem item)
{
return Id.GetHashCode();
}
}
And I have two lists as follows:
var list1 = new List<DlpItem>();
list1.Add(new DlpItem(1));
list1.Add(new DlpItem(2));
var list2 = new List<DlpItem>();
list2.Add(new DlpItem(1));
list2.Add(new DlpItem(2));
var delItems = list1.Except(list2).ToList<DlpItem>();
delItems always has both items in it. What am I missing here?
EDIT: Code now implements IEquatable
public class DlpItem : IEqualityComparer<DlpItem>, IEquatable<DlpItem>
{
public string Text { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
return Id - (obj as DlpItem).Id == 0;
}
public bool Equals(DlpItem a, DlpItem b)
{
return a.Id == b.Id;
}
public bool Equals(DlpItem item)
{
return item != null && Id == item.Id;
}
public int GetHashCode(DlpItem item)
{
return Id.GetHashCode();
}
}
In your example, you don't actually add anything to list2... a simple enough mistake, but there is a more significant issue:
It needs to be IEquatable<T> not an IEqualityComparer<T>; also, you might want to ensure the hashcode can't change; most simply by making Id read-only:
public class DlpItem : IEquatable<DlpItem>
{
public string Text { get; set; }
private readonly int id;
public int Id { get { return id; } }
public DlpItem(int id)
{
Text = "";
this.id = id;
}
public override bool Equals(object obj)
{
return Equals(obj as DlpItem);
}
public bool Equals(DlpItem other)
{
return other != null && this.Id == other.Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}