Inline Comparer - c#

I have a class Person with a Name property.
I have a collection of persons.
I have a method to add a new person but I need to check of the collection already contains the person.
I would like to use coll.Contains(newPerson,[here is the comparer]) where the comparer will make the comparison on the name property.
Is it possible to make the comparison inline (anonymously) without creating a new class implementing IEqualityComparer?

In the case you don't want duplicate Person objects, and want to operate on that collection as a set, you can use a HashSet<Person> instead which when calling its Add method will do the check if such a person already exists. For that to work, you can implement IEquatable<Person> in your class. It would look roughly like this:
public class Person : IEquatable<Person>
{
public Person(string name)
{
Name = name;
}
public string Name { get; private set; }
public bool Equals(Person other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
}
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((Person) obj);
}
public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
public static bool operator ==(Person left, Person right)
{
return Equals(left, right);
}
public static bool operator !=(Person left, Person right)
{
return !Equals(left, right);
}
}
And now you can use it in your HashSet<Person> like this:
void Main()
{
var firstPerson = new Person { Name = "Yuval" };
var secondPerson = new Person { Name = "yuval" };
var personSet = new HashSet<Person> { firstPerson };
Console.WriteLine(personSet.Add(secondPerson)); // Will print false.
}
Note this won't give you the flexibility of multiple comparers, but this way you won't have to create a new class implementing IEqualityComparer<T>.

You can use linq instead.
bool contains = coll.Any(p => p.Name == newPerson.Name);
You can add any condition here as you want. for example as WaiHaLee noted you can make compare ignore case.
bool contains = coll.Any(p => p.Name.Equals(newPerson.Name, StringComparison.OrdinalIgnoreCase));

Related

How should I implement equality checking for classes that have objects of other classes as member?

I have a Student class that has an object of class Name.
At this point, the equality check of the Student class returns false, and I don't know why.
public class Student : IEquatable<Student>
{
public Name Name { get; }
public Student(Name name) => Name = name;
public bool Equals(Student other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(other, null))
return false;
return Name == (other.Name);
}
}
public class Name : IEquatable<Name>
{
public string First { get; }
public Name(string first) => First = first;
public bool Equals(Name other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(other, null))
return false;
return First == other.First;
}
}
var s1 = new Student(new Name("A"));
var s2 = new Student(new Name("A"));
Console.WriteLine(s1.Equals(s2).ToString());
Of course, doing the equality check this way will return true.
var s1 = new Student(new Name("A"));
var s2 = s1;
Console.WriteLine(s1.Equals(s2).ToString());
Can you tell me what I'm doing wrong?
So, == by default for reference types just compares references. Implementing IEquatable<T> does not overload == automatically, you have to do it yourself. string does overload that operator, so it works in Name, but your Name class doesn't, so in Student, the default behaviour is used, which is equivalent to ReferenceEquals().
Implementing IEquatable<T> correctly also means you need to override Equals(object) and GetHashCode(), which you don't do here.
Solution 1 (Recommended)
Since you had implemented IEquatable interface for Name class, just calls .Equals() in Student class.
public class Student : IEquatable<Student>
{
public Name Name { get; }
public Student(Name name) => Name = name;
public bool Equals(Student other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(other, null))
return false;
return Name.Equals(other.Name); // Replaces with .Equals
}
}
Sample .NET Fiddle (Solution 1)
Solution 2
Or you need to implement operator overloading for the == and != operators.
If you use == operator in:
public class Student : IEquatable<Student>
{
...
public bool Equals(Student other)
{
...
return Name == (other.Name);
}
}
public class Name : IEquatable<Name>
{
...
public static bool operator == (Name a, Name b)
{
return a.First == b.First;
}
public static bool operator != (Name a, Name b)
{
return a.First != b.First;
}
}
Sample .NET Fiddle (Solution 2)

How to make Service Fabric Reliable collections case-insensitive?

I have a Stateful Service Fabric service and create, update or read data using IReliableDictionary created with the following code:
var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<string, Entry>>(ReliableDictionaryName);
// Read
using (ITransaction tx = StateManager.CreateTransaction())
{
ConditionalValue<Entry> result = await dictionary.TryGetValueAsync(tx, name);
return result.HasValue ? result.Value : null;
}
// Create or update
using (ITransaction tx = StateManager.CreateTransaction())
{
await dictionary.AddOrUpdateAsync(tx, entry.Name, entry, (key, prev) => entry);
await tx.CommitAsync();
}
It works, but it is case-sensitive.
Is there any way to make Reliable collection store and get data in a case-insensitive way, except for applying .ToLower() to the keys, which is kind of hacky?
This behavior you see is mostly a property of how strings are compared by default in C#. Reliable dictionaries use a key's implementation of IEquatable and IComparable to perform lookups. If the default behavior of string doesn't work for you, you can implement a type that performs string comparisons the way you want. Then, use the new type as the key for your reliable dictionary. You could implement implicit operators to convert between raw strings and the custom type to make usage painless. Here's an example:
using System.Runtime.Serialization;
[DataContract]
public class CaseInsensitiveString : IEquatable<CaseInsensitiveString>,
IComparable<CaseInsensitiveString>
{
#region Constructors
public CaseInsensitiveString(string value)
{
this.Value = value;
}
#endregion
#region Instance Properties
[DataMember]
public string Value
{
get;
set;
}
#endregion
#region Instance Methods
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 this.Equals((CaseInsensitiveString)obj);
}
public override int GetHashCode()
{
return this.Value != null
? this.Value.GetHashCode()
: 0;
}
public int CompareTo(CaseInsensitiveString other)
{
return string.Compare(this.Value,
other?.Value,
StringComparison.OrdinalIgnoreCase);
}
public bool Equals(CaseInsensitiveString other)
{
if (ReferenceEquals(null,
other))
{
return false;
}
if (ReferenceEquals(this,
other))
{
return true;
}
return string.Equals(this.Value,
other.Value,
StringComparison.OrdinalIgnoreCase);
}
#endregion
#region Class Methods
public static bool operator ==(CaseInsensitiveString left,
CaseInsensitiveString right)
{
return Equals(left,
right);
}
public static implicit operator CaseInsensitiveString(string value)
{
return new CaseInsensitiveString(value);
}
public static implicit operator string(CaseInsensitiveString caseInsensitiveString)
{
return caseInsensitiveString.Value;
}
public static bool operator !=(CaseInsensitiveString left,
CaseInsensitiveString right)
{
return !Equals(left,
right);
}
#endregion
}

C# equality with list-based properties

I've read numerous articles related to proper equality in C#:
http://www.loganfranken.com/blog/687/overriding-equals-in-c-part-1/
What is the best algorithm for an overridden System.Object.GetHashCode?
Assume the following sample class:
public class CustomData
{
public string Name { get; set;}
public IList<double> Values = new List<double>();
}
Would it still be the case to compare the Values property using .Equals()? Here is a full equality sample of what I mean:
#region Equality
public override bool Equals(object value)
{
if(Object.ReferenceEquals(null, value)) return false; // Is null?
if (Object.ReferenceEquals(this, value)) return true; // Is the same object?
if (value.GetType() != this.GetType()) return false; // Is the same type?
return IsEqual((CustomData)value);
}
public bool Equals(CustomData obj)
{
if (Object.ReferenceEquals(null, obj)) return false; // Is null?
if (Object.ReferenceEquals(this, obj)) return true; // Is the same object?
return IsEqual(obj);
}
private bool IsEqual(CustomData obj)
{
return obj is CustomData other
&& other.Name.Equals(Name)
&& other.Values.Equals(Values);
}
public override int GetHashCode()
{
unchecked
{
// Choose large primes to avoid hashing collisions
const int HashingBase = (int) 2166136261;
const int HashingMultiplier = 16777619;
int hash = HashingBase;
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, Name) ? Name.GetHashCode() : 0);
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, Values) ? Values.GetHashCode() : 0);
return hash;
}
}
public static bool operator ==(CustomData obj, CustomData other)
{
if (Object.ReferenceEquals(obj, other)) return true;
if (Object.ReferenceEquals(null, obj)) return false; // Ensure that "obj" isn't null
return (obj.Equals(other));
}
public static bool operator !=(CustomData obj, CustomData other) => !(obj == other);
#endregion
List<T>.Equals(List<T> other) will compare references. If you want equality for property Values to be defined as identical sequences of doubles, use the IEnumerable<TSource>.SequenceEqual.(IEnemerable<TSource> other) method (MSDN). See a refactored version of your IsEqual(CustomData obj) below:
private bool IsEqual(CustomData obj)
{
return obj is CustomData other
&& other.Name.Equals(Name)
&& other.Values.SequenceEqual(Values);
}

Merging duplicate elements within an IEnumerable

I currently have an IEnumerable<MyObject> where MyObject has the properties String Name and long Value.
If i was to have within the Enumerable, 10 instances of MyObject, each with a different name and value, with the exception of one having the same name as the other.
Does .NET (or LINQ) have a built in method which will allow me to find the duplicate, and if possible, merge the Value property so that there ends up being only 9 elements within the enumerable, each with a distinct Name and the one that had a duplicate has the Value that is equal to the sum of its self and the duplicate.
So far i have found that the only way to iterate over the entire IEnumerable and look for the duplicates and generate a new IEnumerable of unique items, but this seems untidy and slow.
You can group items by name and project results to 'merged' objects:
objects.GroupBy(o => o.Name)
.Select(g => new MyObject { Name = g.Key, Value = g.Sum(o => o.Value) });
UPDATE: Another option, if new MyObject instantiation is undesired (e.g. you have many properties in this class, or you should preserver references) then you can use aggregation with first item in group as accumulator:
objects.GroupBy(o => o.Name)
.Select(g => g.Skip(1).Aggregate(
g.First(), (a, o) => { a.Value += o.Value; return a; }));
list.GroupBy(e => e.Name).Select(group => new MyObject
{
Name = group.Key,
Value = group.Sum(e => e.Value)
}
)
Update:
Another variant:
list.GroupBy(
e => e.Name,
e => e,
(name, group) => group.Aggregate((result, e) =>
{
result.Value += e.Value;
return result;
}
)
)
I dont know a single method solution but what about:
set.GroupBy(g=>g.Name).Select(g=> new MyObject{Name=g.Key, Value=g.Sum(i=>i.Value)});
Implement interface IEquatable and use Ditinct method. As follow:
internal class Program
{
private static void Main(string[] args)
{
var items = new List<MyClass>
{
new MyClass
{
Name = "Name1",
Value = 50
},
new MyClass
{
Name = "Name2",
Value = 20
},
new MyClass
{
Name = "Name3",
Value = 50
}
};
var distinct = items.Distinct().ToList();
}
}
internal class MyClass : **IEquatable<MyClass>**
{
public String Name { get; set; }
public int Value { get; set; }
**public bool Equals(MyClass other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return this.Value == other.Value;
}
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 this.Equals((MyClass)obj);
}
public override int GetHashCode()
{
return this.Value;
}
public static bool operator ==(MyClass left, MyClass right)
{
return Equals(left, right);
}
public static bool operator !=(MyClass left, MyClass right)
{
return !Equals(left, right);
}**
}

SequenceEqual is true but HashSet.SetEquals is false

I've been experimenting with implementing Set equality (ie List comparisons where the order is irrelevant) and after reading SO questions like this and this, wrote the following simple extension:
public static bool SetEqual<T>(this IEnumerable<T> enumerable, IEnumerable<T> other)
{
if (enumerable == null && other == null)
return true;
if (enumerable == null || other == null)
return false;
var setA = new HashSet<T>(enumerable);
return setA.SetEquals(other);
}
However, I came across a simple data structure for which this approach does not work, while Enumerable.SequenceEqual does.
public class Test : IEquatable<Test>
{
public Guid Id { get; set; }
public List<Test> RelatedTest { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(Test)) return false;
return Equals((Test)obj);
}
public bool Equals(Test other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Id.Equals(Id) &&
RelatedTest.SetEqual(other.RelatedTest);
}
}
Given this object, this test succeeds:
[Test]
public void SequenceEqualTest()
{
var test1 = new List<Test> {new Test()};
var test2 = new List<Test> {new Test() };
Assert.That(test1.SequenceEqual(test2), Is.True);
}
But this test fails:
[Test]
public void SetEqualTest()
{
var test1 = new List<Test> {new Test()};
var test2 = new List<Test> {new Test()};
Assert.That(test1.SetEqual(test2), Is.True);
}
Does anyone have an explanation?
Yes, you didn't override GetHashCode in your Test class, so the HashSet is unable to effectively group items into buckets according to possible equality. See this question for more details: Why is it important to override GetHashCode when Equals method is overridden?

Categories

Resources