Can I specify my explicit type comparator inline?
My MiscUtil library contains a ProjectionComparer to build an IComparer<T> from a projection delegate. It would be the work of 10 minutes to make a ProjectionEqualityComparer to do the same thing.
EDIT: Here's the code for ProjectionEqualityComparer:
using System;
using System.Collections.Generic;
/// <summary>
/// Non-generic class to produce instances of the generic class,
/// optionally using type inference.
/// </summary>
public static class ProjectionEqualityComparer
{
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// </summary>
/// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting
/// each element to its key, and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// The ignored parameter is solely present to aid type inference.
/// </summary>
/// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="ignored">Value is ignored - type may be used by type inference</param>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting
/// each element to its key, and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>
(TSource ignored,
Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
/// <summary>
/// Class generic in the source only to produce instances of the
/// doubly generic class, optionally using type inference.
/// </summary>
public static class ProjectionEqualityComparer<TSource>
{
/// <summary>
/// Creates an instance of ProjectionEqualityComparer using the specified projection.
/// </summary>
/// <typeparam name="TKey">Type parameter for the keys to be compared,
/// after being projected from the elements</typeparam>
/// <param name="projection">Projection to use when determining the key of an element</param>
/// <returns>A comparer which will compare elements by projecting each element to its key,
/// and comparing keys</returns>
public static ProjectionEqualityComparer<TSource, TKey> Create<TKey>(Func<TSource, TKey> projection)
{
return new ProjectionEqualityComparer<TSource, TKey>(projection);
}
}
/// <summary>
/// Comparer which projects each element of the comparison to a key, and then compares
/// those keys using the specified (or default) comparer for the key type.
/// </summary>
/// <typeparam name="TSource">Type of elements which this comparer
/// will be asked to compare</typeparam>
/// <typeparam name="TKey">Type of the key projected
/// from the element</typeparam>
public class ProjectionEqualityComparer<TSource, TKey> : IEqualityComparer<TSource>
{
readonly Func<TSource, TKey> projection;
readonly IEqualityComparer<TKey> comparer;
/// <summary>
/// Creates a new instance using the specified projection, which must not be null.
/// The default comparer for the projected type is used.
/// </summary>
/// <param name="projection">Projection to use during comparisons</param>
public ProjectionEqualityComparer(Func<TSource, TKey> projection)
: this(projection, null)
{
}
/// <summary>
/// Creates a new instance using the specified projection, which must not be null.
/// </summary>
/// <param name="projection">Projection to use during comparisons</param>
/// <param name="comparer">The comparer to use on the keys. May be null, in
/// which case the default comparer will be used.</param>
public ProjectionEqualityComparer(Func<TSource, TKey> projection, IEqualityComparer<TKey> comparer)
{
if (projection == null)
{
throw new ArgumentNullException("projection");
}
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
this.projection = projection;
}
/// <summary>
/// Compares the two specified values for equality by applying the projection
/// to each value and then using the equality comparer on the resulting keys. Null
/// references are never passed to the projection.
/// </summary>
public bool Equals(TSource x, TSource y)
{
if (x == null && y == null)
{
return true;
}
if (x == null || y == null)
{
return false;
}
return comparer.Equals(projection(x), projection(y));
}
/// <summary>
/// Produces a hash code for the given value by projecting it and
/// then asking the equality comparer to find the hash code of
/// the resulting key.
/// </summary>
public int GetHashCode(TSource obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
return comparer.GetHashCode(projection(obj));
}
}
And here's a sample use:
var f3 = f1.Except(f2, ProjectionEqualityComparer<Foo>.Create(a => a.key));
Inline Comparer
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>
.
Is there a concise/inline way to create Set values without explicitly naming their type?
In the big picture I think this is something you might just have to get used to. The notation is a little heavier than some other languages, but OCaml modules are also much more expressive than those of other languages.
That said, you can avoid giving a name to the module using let open
:
# let numbers = let open Set.Make(Int) in empty;;
val numbers : Set.Make(Int).t = <abstr>
# let morenumbers = let open Set.Make(Int) in add 14 numbers;;
val morenumbers : Set.Make(Int).t = <abstr>
At least, this works for me.
The essence is that you need to use a place in the syntax where a module name can go. In the expression syntax Set.Make(Int).t
looks like a value constructor. (Or anyway this is how I interpret the behavior.)
Update
As a commenter far wiser than I has pointed out, it's good to keep in mind that this is not good style in OCaml. As I said, in the big picture you probably just want to get used to giving names to modules.
Enumerable.Except does not use my custom comparer
Similar to Marc I just tested this, everything is being called just fine, my guess is that you are caught by the LINQ deferred execution, notice the ToArray in my code.
Note, when tracing through this I noticed GetHashCode is never called on null objects in the comparer.
Keep in mind, MiscUtil has an awesome way for you to do this stuff inline, see: Can I specify my explicit type comparator inline?
Or you could adapt this to Except: Distinct list of objects based on an arbitrary key in LINQ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1 {
public class BusinessObject {
public Guid Guid { get; set; }
}
public class BusinessObjectGuidEqualityComparer<T> : IEqualityComparer<T> where T : BusinessObject {
#region IEqualityComparer<T> Members
public bool Equals(T x, T y) {
return (x == null && y == null) || (x != null && y != null && x.Guid.Equals(y.Guid));
}
/// </exception>
public int GetHashCode(T obj) {
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.GetHashCode();
}
#endregion
}
class Program {
static void Main(string[] args) {
var comparer = new BusinessObjectGuidEqualityComparer<BusinessObject>();
List<BusinessObject> list1 = new List<BusinessObject>() {
new BusinessObject() {Guid = Guid.NewGuid()},
new BusinessObject() {Guid = Guid.NewGuid()}
};
List<BusinessObject> list2 = new List<BusinessObject>() {
new BusinessObject() {Guid = Guid.NewGuid()},
new BusinessObject() {Guid = Guid.NewGuid()},
null,
null,
list1[0]
};
var toRemove = list1.Except(list2, comparer).ToArray();
var toAdd = list2.Except(list1, comparer).ToArray();
// toRemove.Length == 1
// toAdd.Length == 2
Console.ReadKey();
}
}
}
Overload custom comparator to std::map
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker(Comparator(stocksDict));
this defines a member function named stocksTicker
that takes a stocksDict
argument of type Comparator
and returns a std::map
.
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker{Comparator{stocksDict}};
This defines a member variable stocksTicker
which, by default, is initialized with a Comparator
, which in turn was initialized with the member variable stocksDict
.
I assume you want the second.
Your syntax was partway between the two. Whatever compiler you had got confused by this.
Live example
You should StockTicker(StockTicker &&)=delete
and StockTicker& operator=(StockTicker &&)=delete
, as maps containing references to their containing class are not safe to move or copy.
Generating an efficient move here is tricky. I suspect C++17 node splicing might make it possible to do it. You might have to embed a std::shared_ptr<stocksDict*>
(yes, a shared pointer to a pointer), and use .key_comp
to reseat the stocksDict
in the target.
Using custom std::set comparator
1. Modern C++20 solutionauto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;
auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;
We use lambda function as comparator. As usual, comparator should return boolean value, indicating whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
Online demo
2. Modern C++11 solutionauto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);
auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);
Before C++20 we need to pass lambda as argument to set constructor
Online demo
3. Similar to first solution, but with function instead of lambda
Make comparator as usual boolean function
bool cmp(int a, int b) {
return ...;
}
Then use it, either this way:
std::set<int, decltype(cmp)*> s(cmp);
Online demo
or this way:
std::set<int, decltype(&cmp)> s(&cmp);
Online demo
4. Old solution using struct with ()
operatorstruct cmp {
bool operator() (int a, int b) const {
return ...
}
};
// ...
// later
std::set<int, cmp> s;
struct cmp {
bool operator() (int a, int b) const {
return ...
}
};
// ...
// later
std::set<int, cmp> s;
Online demo
5. Alternative solution: create struct from boolean function
Take boolean function
bool cmp(int a, int b) {
return ...;
}
And make struct from it using std::integral_constant
#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
Finally, use the struct as comparator
std::set<X, Cmp> set;
Online demo
LINQ GroupBy on multiple ref-type fields; Custom EqualityComparer
The problem here is that your key type is anonymous, which means you can't declare a class that implements IEqualityComparer<T>
for that key type. While it would be possible to write a comparator which compared anonymous types for equality in a custom manner (via a generic method, delegates and type inference), it wouldn't be terribly pleasant.
The two simplest options are probably:
- Make the anonymous type "just work" by overriding Equals/GetHashCode in
PeriodEndDto
andComponentDto
. If there's a natural equality you'd want to use everywhere, this is probably the sanest option. I'd recommend implementingIEquatable<T>
as well - Don't use an anonymous type for grouping - use a named type, and then you can either override
GetHashCode
andEquals
on that, or you could write a custom equality comparer in the normal way.
EDIT: ProjectionEqualityComparer
wouldn't really work. It would be feasible to write something similar though - a sort of CompositeEqualityComparer
which allowed you create an equality comparer from several "projection + comparer" pairs. It would be pretty ugly compared with the anonymous type though.
Any explanation why java 8 lambda expression works using Function variable and not works inline
With the variable typed to Function<Book, Integer>
the compiler knows the exact generic types. This information is missing in the inline version. Specifying explicit type parameters come to help then as follows:
.sorted(
Comparator.<Book, Integer> comparing( ...
See also this answer.
Related Topics
How to Get Little Endian Data from Big Endian in C# Using Bitconverter.Toint32 Method
What Is Imex Within Oledb Connection Strings
Help with a Oledb Connection String for Excel Files
C# When Should I Use List and When Should I Use Arraylist
Passing Arrays by Value and by Reference
Incremental JSON Parsing in C#
Change Border Color in Textbox C#
Gps Socket Communication (Concox)
Handling Executescalar() When No Results Are Returned
What Is the Max Limit of Data into List<String> in C#
Is It Ok to Use a String as a Lock Object
Pulling Data from a Webpage, Parsing It for Specific Pieces, and Displaying It
Lambda Expression Using Foreach Clause
How to Dispose My Filestream When Implementing a File Download in ASP.NET
How to Pass User Defined Table Type as Stored Procedured Parameter in C#
High Quality Jpeg Compression with C#
Is There Any Way in C# to Override a Class Method with an Extension Method