I have a class that I use to describe a XYZ coordinate along with 3 properties.
The class looks like this:
class dwePoint
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
protected bool Equals(dwePoint other)
{ //This doesnt seem to work
if(Prop1== "Keep")
{
return false;
}
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Prop1.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
}
Checking the XYZ on the Equals, I can filter out duplicates only based on the actual coordinates, ignoring the properties.
In my code, I use a list, so I call the List.Distinct()
Now there is one thing I cant figure out yet:
It is possible there are 2 points with the same XYZ, but with different properties.
In that case I always want to keep the one with a specific string (for example "Keep") and always remove the one that has some other value.
I was already trying some if statements, without any luck...
How should I handle this ?
What you want is not really possible with Distinct since it uses your Equals as it's only input for equality (as it should), so it has no way to even be aware that there could be a difference between the objects.
I think it would be a better design for you to compose your class using a new class, e.g. Point3D containing your coordinates and your 3 properties. Then you can group by the point, and for everything that has more than one equal point, apply your own logic as to which to keep.
In code:
class Point3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
// Equals and get hash code here
}
class dwePoint
{
Point3D Coordinate {get;}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Filter list by applying grouping and your custom logic
points = points.GroupBy(p => p.Coordinate)
.Select(x =>
x.OrderByDescending(p => p.Prop1 == "Keep") // Sort so the element you want to keep is first
.First() // If there is only one element, the ordering will not matter
).ToList();
If you really want, the GroupBy also works with your current class design, since only the coordinates takes part in the Equals.
Your GetHashCode runs into a nullref if Prop1 == null, you should fix that.
And another solution: use Aggregate and Lamda to distinct your list. The Equal() on your class only compares the X,Y and Z - the Aggregate lambda makes sure you keep what you want. Probably put the Aggregate in a extension method or Function.
static void Main()
{
List<dwePoint> points = new List<dwePoint>();
// Testdata
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
for (int z = 0; z < 3; z++)
{
points.Add(new dwePoint { X = x, Y = y, Z = z });
if (x == y && x == z) // and some duplicates to Keep
points.Add(new dwePoint { X = x, Y = y, Z = z, Prop1 = "Keep" });
}
// prefer the ones with "Keep" in Prop1
var distincts = points.Aggregate(new HashSet<dwePoint>(), (acc, p) =>
{
if (acc.Contains(p))
{
var oldP = acc.First(point => point.X == p.X && point.Y == p.Y && point.Z == p.Z);
if (oldP.Prop1 == "Keep")
{
// do nothing - error, second point with "keep"
}
else
{
acc.Remove(oldP);
acc.Add(p); // to use this ones other props later on ....
}
}
else
acc.Add(p);
return acc;
}).ToList();
Console.WriteLine(string.Join(" - ", points));
Console.WriteLine(string.Join(" - ", distincts));
Console.ReadLine();
}
private class dwePoint
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as dwePoint);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
return hashCode;
}
}
public override string ToString() => $"{X}-{Y}-{Z}-{Prop1}-{Prop2}-{Prop3}";
protected bool Equals(dwePoint other)
{
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
}
I had the same answer in mind as driis provided..
But, I would like to provide you with another option.
You can write a Distinct on your own as an extension method.
Here is a work around. I wrote a sample code, so that you can understand better.
static class Program
{
static void Main(string[] args)
{
List<Abc> list = new List<Abc>()
{
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
new Abc()
{
a = 5,
b = 6,
s = "Keep"
},
new Abc()
{
a = 9,
b = 10,
s = "Keep"
},
new Abc()
{
a = 5,
b = 6,
s = "Phew"
},
new Abc()
{
a = 9,
b = 10,
s = "Phew"
},
};
list = list.MyDistinct();
}
// Extension Method
public static List<Abc> MyDistinct(this List<Abc> list)
{
List<Abc> newList = new List<Abc>();
foreach (Abc item in list)
{
Abc found = newList.FirstOrDefault(x => x.Equals(item));
if (found == null)
{
newList.Add(item);
}
else
{
if (found.s != "Keep" && item.s == "Keep")
{
newList.Remove(found);
newList.Add(item);
}
}
}
return newList;
}
}
class Abc
{
public int a, b;
public string s;
public override bool Equals(object obj)
{
Abc other = obj as Abc;
return a == other.a && b == other.b;
}
public override int GetHashCode()
{
return a.GetHashCode() ^ b.GetHashCode();
}
}
Hope it will help you..
Related
I have these classes:
public class AlertEvaluation
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
}
public class EvaluatedTag
{
public string Id { get; set; }
public string Name { get; set; }
}
And I would like to get a list of alert evaluations grouped by AlertId, and by EvaluatedTags, meaning that I would like to compare and group evaluations that not only have the same AlertId, but to also have the same list of EvaluatedTags. (And also get the last evaluation in time)
I tried this:
var evaluationsGroupedAndOrdered = evaluations.GroupBy(x => new { x.AlertSettingId, x.EvaluatedLabels })
.Select(x => x.OrderByDescending(z => z.EvaluationDate ).FirstOrDefault()).ToList();
But of course, the comparing of list properties like that did not work.
I read something about adding an equality comparer in GroupBy, which would mean comparing the lists inside the objects right? But I'm not sure of how to implement it in the right way.
I tried (based on GroupBy on complex object (e.g. List<T>)) :
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
return x.AlertId == y.AlertId && x.EvaluatedTags.OrderBy(val => val.Name).SequenceEqual(y.EvaluatedTags.OrderBy(val => val.Name));
}
public int GetHashCode(AlertSettingEvaluation x)
{
return x.AlertId.GetHashCode() ^ x.EvaluatedTags.Aggregate(0, (a, y) => a ^ y.GetHashCode());
}
}
But did not work either.. Maybe because my list EvaluatedTags is not a list of strings but of individual objects.
Does anybody have a nice solution for this?
A typical way to compare two lists is to use the System.Linq exension method, SequenceEquals. This method returns true if both lists contain the same items, in the same order.
In order to make this work with an IEnumerable<EvaluatedTag>, we need to have a way to compare instances of the EvaluatedTag class for equality (determining if two items are the same) and for sorting (since the lists need to have their items in the same order).
To do this, we can override Equals and GetHashCode and implement IComparable<EvaluatedTag> (and might as well do IEquatable<EvaluatedTag> for completeness):
public class EvaluatedTag : IEquatable<EvaluatedTag>, IComparable<EvaluatedTag>
{
public string Id { get; set; }
public string Name { get; set; }
public int CompareTo(EvaluatedTag other)
{
if (other == null) return -1;
var result = string.CompareOrdinal(Id, other.Id);
return result == 0 ? string.CompareOrdinal(Name, other.Name) : result;
}
public bool Equals(EvaluatedTag other)
{
return other != null &&
string.Equals(other.Id, Id) &&
string.Equals(other.Name, Name);
}
public override bool Equals(object obj)
{
return Equals(obj as EvaluatedTag);
}
public override int GetHashCode()
{
return Id.GetHashCode() * 17 +
Name.GetHashCode() * 17;
}
}
Now we can use this in the custom comparer you have in your question, for sorting and comparing the EvaluatedTags:
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
// Return true if the AlertIds are equal, and the EvaluatedTags
// contain the same items (call OrderBy to ensure they're in
// the same order before calling SequenceEqual).
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
if (x == null) return y == null;
if (y == null) return false;
if (!string.Equals(x.AlertId, y.AlertId)) return false;
if (x.EvaluatedTags == null) return y.EvaluatedTags == null;
if (y.EvaluatedTags == null) return false;
return x.EvaluatedTags.OrderBy(et => et)
.SequenceEqual(y.EvaluatedTags.OrderBy(et => et));
}
// Use the same properties in GetHashCode that were used in Equals
public int GetHashCode(AlertEvaluation obj)
{
return obj.AlertId?.GetHashCode() ?? 0 * 17 +
obj.EvaluatedTags?.Sum(et => et.GetHashCode() * 17) ?? 0;
}
}
And finally we can pass your AlertEvaluationComparer to the GroupBy method to group our items:
var evaluationsGroupedAndOrdered = evaluations
.GroupBy(ae => ae, new AlertEvaluationComparer())
.OrderBy(group => group.Key.EvaluationDate)
.ToList();
Here's a go at it, getting away from Linq a bit to make it easier to build the groups one at a time while leveraging sorting:
// Build groups by using a combination of AlertId and EvaluatedTags hashcode as group key
var groupMap = new Dictionary<string, SortedSet<AlertEvaluation>>();
foreach (var item in evals)
{
var combinedKey = item.AlertId + EvaluatedTag.GetCollectionHashCode(item.EvaluatedTags);
if (groupMap.TryGetValue(combinedKey, out SortedSet<AlertEvaluation>? groupItems))
{
// Add to existing group
groupItems.Add(item);
}
else
{
// Create new group
groupMap.Add(combinedKey, new SortedSet<AlertEvaluation> { item });
}
}
// Get a list of groupings already sorted ascending by EvaluationDate
List<SortedSet<AlertEvaluation>>? groups = groupMap.Values.ToList();
This assumes that the classes implement IComparable and Equals/GetHashCode to facilitate sorting:
public class AlertEvaluation : IComparable<AlertEvaluation>
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
// Used by SortedSet
public int CompareTo(AlertEvaluation? other)
{
if (other is null)
{
return 1;
}
return EvaluationDate.CompareTo(other.EvaluationDate);
}
}
public class EvaluatedTag : IEquatable<EvaluatedTag?>
{
public string Id { get; set; }
public string Name { get; set; }
public bool Equals(EvaluatedTag? other) => other != null && Id == other.Id && Name == other.Name;
public override int GetHashCode() => HashCode.Combine(Id, Name);
// Helper to get a hash of item collection
public static int GetCollectionHashCode(ICollection<EvaluatedTag> items)
{
var code = new HashCode();
foreach (var item in items.OrderBy(i => i.Id))
{
code.Add(item);
}
return code.ToHashCode();
}
}
By the way, I'm using the fancy new HashCode class in .NET Core to override hash codes.
I created two classes almost identical. Both represent a Pair (x,y) but in one of them I overrode the GetHashCode and Equals methods. I was told that when the HashCode is different the Collections takes them as different elements and does not even bother to actually compare them with the equals. However, it turns out that I implemented an EqualityComparer for the class that do not override the GetHashCode and Equals and everything works fine even when the HashCodes are still different.
Take a look at my Console Project:
using System;
using System.Collections.Generic;
using System.Linq;
namespace matrixExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Same Hash but no insertion: as expected");
HashSet<MyPair> hash = new HashSet<MyPair>();
MyPair one = new MyPair { X = 10, Y = 2 };
MyPair copyOfOne = new MyPair { X = 10, Y = 2 };
Console.WriteLine(one.GetHashCode() + " " + hash.Add(one));
Console.WriteLine(copyOfOne.GetHashCode() + " " + hash.Add(copyOfOne));
Console.WriteLine("-----------------------------------------");
Console.WriteLine("Different Hash but no insertion! why?");
HashSet<MyPairWithoutOverride> hash2 = new HashSet<MyPairWithoutOverride>(new SameHash());
MyPairWithoutOverride a1 = new MyPairWithoutOverride { X = 10, Y = 2 };
MyPairWithoutOverride a1copy = new MyPairWithoutOverride { X = 10, Y = 2 };
Console.WriteLine(a1.GetHashCode() + " " + hash2.Add(a1));
Console.WriteLine(a1copy.GetHashCode() + " " + hash2.Add(a1copy));
}
public class MyPair
{
public int X { get; set; }
public int Y { get; set; }
public override int GetHashCode()
{
return X * 10000 + Y;
}
public override bool Equals(object obj)
{
MyPair other = obj as MyPair;
return X == other.X && Y == other.Y;
}
}
public class MyPairWithoutOverride
{
public int X { get; set; }
public int Y { get; set; }
}
public class SameHash : EqualityComparer<MyPairWithoutOverride>
{
public override bool Equals(MyPairWithoutOverride p1, MyPairWithoutOverride p2)
{
return p1.X == p2.X && p1.Y == p2.Y;
}
public override int GetHashCode(MyPairWithoutOverride i)
{
return base.GetHashCode();
}
}
}
}
Your problem is here
public override int GetHashCode(MyPairWithoutOverride i)
{
return base.GetHashCode();
}
You're returning base.GetHashCode() which is actually the hash code of the SameHash class. So you actually are returning the same hash code every single time.
If you return i.GetHashCode() then it will behave as expected.
Sorry if the question is redundant, but I couldn't find a solution for my particular case.
Please consider this block of code:
public interface IPoint {}
public class GazePoint : IPoint {}
public Point AvgPoint(IEnumerable<IPoint> locations) {}
List<GazePoint> gazePoints = new List<GazePoint>();
//...
// this doesn't work:
Point avg = AvgPoint(gazePoints);
Could you please explain why it doesn't work (I was assuming C# 4.0 has solved this issue) and how can I change the signature of AvgPoint() method to make it possible to receive different implementations of IPoint. (I don't want to cast gazePoints collection to another type of collection, because it is in a big loop, and I'm concern about the performance.
[Update]: I had defined GazePoint as struct, and that was the source of problem. Still, I don't know why struct is not working here.
I'm not sure what the exact problem is you're having, but here's how it worked for me:
First, some actual class implementations:
public interface IPoint
{
int X { get; set; }
int Y { get; set; }
}
public class Point : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public Point()
{
}
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public class GazePoint : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public GazePoint()
{
}
public GazePoint(int x, int y)
{
X = x;
Y = y;
}
}
Then an actual AvgPoint method implementation:
public static Point AvgPoint(IEnumerable<IPoint> locations)
{
if (locations == null || !locations.Any()) return new Point(0, 0);
return new Point((int) locations.Average(l => l.X),
(int) locations.Average(l => l.Y));
}
And finally a few tests:
public static void Main()
{
var points = new List<Point>
{
new Point(1, 2),
new Point(3, 4)
};
var gazePoints = new List<GazePoint>
{
new GazePoint(1, 2),
new GazePoint(3, 4)
};
Point avgPoint = AvgPoint(points);
Point avgGazePoint = AvgPoint(gazePoints);
Console.WriteLine("Average Point = {0}, {1}", avgPoint.X, avgPoint.Y);
Console.WriteLine("Average GazePoint = {0}, {1}", avgGazePoint.X, avgGazePoint.Y);
}
If your goal is to have the method return the average in the same type that was passed in, you can make it generic like so:
public static T AvgPoint<T>(IEnumerable<T> locations) where T : IPoint, new()
{
if (locations == null || !locations.Any()) return new T {X = 0, Y = 0};
return new T {X = (int) locations.Average(l => l.X),
Y = (int) locations.Average(l => l.Y)};
}
I need to create a function to evaluate queries for some rules before executing them. Here's the code:
public class DataInfo
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
static class Program
{
static void Main()
{
var data = new DataInfo()
{
A = 10,
B = 5,
C = -1
};
// the result should be -1
int result = Calcul<DataInfo>(data, x => x.A / x.B + x.C);
}
static int Calcul<T>(T data, Expression<Func<T, int>> query)
{
// PSEUDO CODE
// if one property used in the query have a
// value of -1 or -2 then return 0
// {
// return 0;
// }
// if one property used in the query have a
// value of 0 AND it is used on the right side of
// a Divide operation then return -1
// {
// return -1;
// }
// if the query respect the rules, apply the query and return the value
return query.Compile().Invoke(data);
}
}
In the previous code, the calcul want to divide A(10) with B(5) and then add C(-1). The rules said that if one property used in the query have a value of -1 or -2, return 0. So in this example, the value return should be -1. If the query respect the rules, then apply the query on the data and return the value.
So how can i extract the properties used in the query and test the value used in them before appying the query on the data?
You need to use an ExpressionVisitor to test the property values. Here is an example of how you could implement the logic.
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
// HasDivideByZero - the result should be -1
int result1 = Calcul<DataInfo>(new DataInfo { A = 10, B = 0, C = 1 }, x => x.A / x.B + x.C);
// HasNegative - the result should be 0
int result2 = Calcul<DataInfo>(new DataInfo { A = 10, B = 5, C = -1 }, x => x.A / x.B + x.C);
// the result should be 3
int result3 = Calcul<DataInfo>(new DataInfo { A = 10, B = 5, C = 1 }, x => x.A / x.B + x.C);
}
static int Calcul<T>(T data, Expression<Func<T, int>> query)
{
if (NegativeValueChecker<T>.HasNegative(data, query))
{
return 0;
}
if (DivideByZeroChecker<T>.HasDivideByZero(data, query))
{
return -1;
}
return query.Compile().Invoke(data);
}
}
class DivideByZeroChecker<T> : ExpressionVisitor
{
private readonly T _data;
private bool _hasDivideByZero;
public static bool HasDivideByZero(T data, Expression expression)
{
var visitor = new DivideByZeroChecker<T>(data);
visitor.Visit(expression);
return visitor._hasDivideByZero;
}
public DivideByZeroChecker(T data)
{
this._data = data;
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (!this._hasDivideByZero && node.NodeType == ExpressionType.Divide)
{
var rightMemeberExpression = (MemberExpression)node.Right;
var propertyInfo = (PropertyInfo)rightMemeberExpression.Member;
var value = Convert.ToInt32(propertyInfo.GetValue(this._data, null));
this._hasDivideByZero = value == 0;
}
return base.VisitBinary(node);
}
}
class NegativeValueChecker<T> : ExpressionVisitor
{
private readonly T _data;
public bool _hasNegative;
public static bool HasNegative(T data, Expression expression)
{
var visitor = new NegativeValueChecker<T>(data);
visitor.Visit(expression);
return visitor._hasNegative;
}
public NegativeValueChecker(T data)
{
this._data = data;
}
protected override Expression VisitMember(MemberExpression node)
{
if (!this._hasNegative)
{
var propertyInfo = (PropertyInfo)node.Member;
var value = Convert.ToInt32(propertyInfo.GetValue(this._data, null));
this._hasNegative = value < 0;
}
return base.VisitMember(node);
}
}
class DataInfo
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
}
Get a look at the source of Moq - http://code.google.com/p/moq/.
I have a class
class TestFixture
{
public string a { get; set; }
public int b { get; set; }
public int c { get; set; }
public string d { get; set; }
public string e { get ; set ; }
public int f { get; set; }
public int g { get; set; }
public bool h { get; set; }
public string i { get; set; }
public bool j { get; set; }
public bool k { get; set; }
public TestFixture()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
}
I want to define new TestFixture as SO
new TestFixture { a = "", b = 1, c=2, d="" };
while the rest of properties should be auto defined as it written in constructor.
Is it possible ?
Yes, this is possible. Using an object initializer does not skip calling the constructor.
TestFixture fixture = new TestFixture() // or just new TestFixture { ... }
{
a = "",
b = 1,
c = 2,
d = ""
};
This will call the constructor you've defined and then set a, b, c, and d in your object initializer.
Pop a breakpoint in your constructor and run your debugger. This is should show you how and when things in your code are called.
Debugging in Visual Studio
Refactored:
public class TestFixture
{
public string a { get; set; }
public int b { get; set; }
public int c { get; set; }
public string d { get; set; }
// dosomething should check for null strings
public string e { get { return dosomething(a, b); } }
public int f { get; set; }
public int g { get; set; }
public bool h
{
get { return TestName.Equals("1") && b.Equals("2") ? 1000 : 1; }
}
public string i { get; set; }
public bool j { get { return a != null && a.Equals("FOT"); } }
public bool k { get; set; }
public TestFixture(string a, int b, int c, string d)
: this()
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public TestFixture()
{
f = false;
g = DateTime.Now.ToString("yyMMddhhmmss");
i = 10000000;
k = false;
}
}
#hunter's answer is correct, you can use object initializer syntax, and those properties will be set after your constructor runs. However, I'd like to point out some flaws you may have with your code
public TestFixture()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
This code does not set a or b, but you have things that depend on their values (e, g, j). Object initializer syntax is not going to be useful here, you have to have proper defaults for these values if other values in the constructor will depend upon them.
As an example, when you write var obj = new Bar() { A = "foo" };, that will expand to
var obj = new Bar(); // constructor runs
obj.A = "Foo"; // a is set
Clearly, the code in the constructor that looks at A will not see the value "Foo". If you need it to see this value, object initialization strategy is not going to help. You need a constructor overload that takes the value to be stored in A.
var obj = new Bar("Foo");
If I understand you right, you would like to the a, b, c and d properties to be initialized with the given values before the constructor runs. Unfortunately, that is not possible this way, because the default constructor always runs before the object intializers.
I advise you to do something like this instead:
class TestFixture
{
//... properties
public TestFixture()
{
this.init();
}
public TestFixture(string a, int b, int c, string d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.init();
}
private void init()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
}
This way you can init the a, b, c and d properties before the other initializer code runs.