HashSet sort by property but set unicity with another - c#

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}");
}

Related

Can we use c# indexer to index class properties?

I want to index class properties like an array.
Public class Foo
{
propery p1{get;set;}
propery p3{get;set;}
propery p3{get;set;}
.
.
.
.
}
I wan to index every propery like an array
FOO.p1=Value
Foo[0]=Value(index 0 refers to p1)
I don't know much about the database, where there might have been a ready-made solution. But at least you can do it by reflection in this way:
using System.Reflection;
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class IndexedPropertyAttribute : Attribute
{
readonly int index;
public IndexedPropertyAttribute(int index)
{
this.index = index;
}
public int Index
{
get { return index; }
}
}
public abstract class WithIndexedProperties
{
private Lazy<IReadOnlyDictionary<int, PropertyInfo>> properties;
protected WithIndexedProperties()
{
properties = new Lazy<IReadOnlyDictionary<int, PropertyInfo>>(
() => {
var linq = from prop in this.GetType().GetProperties()
let attr = prop.GetCustomAttributes(typeof(IndexedPropertyAttribute), true)
where attr.Length is 1
select (((IndexedPropertyAttribute)attr[0]).Index, prop);
return linq.ToDictionary(p => p.Index, p => p.prop);
});
}
public object? this[int propertyIndex]
{
get
{
return properties.Value[propertyIndex].GetValue(this);
}
set
{
properties.Value[propertyIndex].SetValue(this, value);
}
}
}
And there is an example:
Clss obj = new Clss();
obj[0] = "ABC";
obj[2] = 222;
obj[4] = 444;
// Here obj.A will be "ABC", obj.B will be 444 and obj.C will be 222.
public class Clss : WithIndexedProperties
{
[IndexedProperty(0)]
public string? A { get; init; }
[IndexedProperty(4)]
public int B { get; init; }
[IndexedProperty(2)]
public int C { get; init; }
}
I think you need to do something like this. The code below is very generalized solution to your question and I might need some customization for yourself
using System.Reflection;
public class ReflectionBasedIndexedType
{
public int A1 { get; set; } = 10;
public int A2 { get; set; } = 20;
public string SomeString => "Hello There";
private readonly Dictionary<string, object> _underlyingCollection;
public object this[string name] => _underlyingCollection[name];
public ReflectionBasedIndexedType()
{
_underlyingCollection = GetUnderlyingCollection();
}
private Dictionary<string, object> GetUnderlyingCollection()
{
Dictionary<string, object> container = new();
// get the properties defined in the class, I am filtering
// with constraint that, I want get only public and class level
// Properties, which means I won't get any private/protected or
// static properties if there is defined such in the class
// also where filters out indexer property, which we have defined
// inside this class, without this line, there will be exception
IEnumerable<PropertyInfo> properties = GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.GetIndexParameters().Length == 0);
foreach (PropertyInfo property in properties)
{
container.Add(property.Name, property.GetValue(this)!);
}
return container;
}
}
and than use case will be like this
ReflectionBasedIndexedType rbit = new();
var a1 = rbit[nameof(rbit.A1)];
var a2 = rbit[nameof(rbit.A2)];
var str = rbit[nameof(rbit.SomeString)];
Console.WriteLine(a1);
Console.WriteLine(a2);
Console.WriteLine(str);
and output in the console will be this
10
20
Hello There
I think you have two ways at least.
The first one, is #Swapper mentioned, yo can use reflection. In this way, the class is normal and you have to write your hug code in where you want to use that class.
The second way is easier but a little fuzy. You can use dynamic type. If you know how to use it, that's ok. Otherwise please let me know, then I will create a sample code for you.

Create reference to a primitive type field in class

I have a few classes which have some primitive fields and I would like to create a generalized wrapper for them in order to access their fields. This wrapper should somehow contain a reference to the fields of my classes so that I can read/write the values of these fields. The idea is to create a genralized architecture for these classes so that I dont have to write code for each of them. The classes have fields which have a number in them which will be used as an Id to access the fields.
This is some example code that might shed some light on my requirement. What I want in the end is to change the value of some field in the object of Fancy1 class without accessing the object itself but through its wrapper.
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
class Fancy2
{
public double level4;
public bool isEnable4;
public double level6;
public bool isEnable6;
public double level7;
}
class FieldWrapper
{
public int id { get; set; }
public object level { get; set; }
public object isEnabled { get; set; }
public FieldWrapper(int id, object level, object isEnabled)
{
this.id = id;
this.level = level;
this.isEnabled = isEnabled;
}
}
class FancyWrapper
{
private Fancy scn;
public FancyWrapper(Fancy scn)
{
if (!(scn is Fancy))
throw new ArgumentException(scn.GetType().FullName + " is not a supported type!");
this.scn = scn;
}
private Dictionary<int, FieldWrapper> fieldLut = new Dictionary<int, FieldWrapper>();
private List<FieldWrapper> _fields { get { return fieldLut.Values.ToList(); } }
public List<FieldWrapper> fields
{
get
{
if (_fields.Count == 0)
{
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(double))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut.Add(satId, new FieldWrapper(satId, fieldInfo.GetValue(scn), true));
}
}
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(bool))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut[satId].isEnabled = fieldInfo.GetValue(scn);
}
}
}
return _fields;
}
}
private int getIdNr(string name)
{
System.Text.RegularExpressions.Match m = System.Text.RegularExpressions.Regex.Match(name, #"\d+");
return Int32.Parse(m.Value);
}
}
class Program
{
static void Main(string[] args)
{
Fancy1 fancy = new Fancy1();
fancy.level1 = 1;
fancy.isEnable1 = true;
fancy.level2 = 2;
fancy.isEnable2 = false;
fancy.level3 = 3;
FancyWrapper wrapper = new FancyWrapper(fancy);
wrapper.fields[2].level = 10;
// fancy.level2 should somehow get the value I set via the wrapper
Console.WriteLine(fancy.level2);
Console.ReadLine();
}
}
EDIT: Fancy classes cannot be changed since they are part of an interface!
Depending on how many Fancy classes you are dealing with, you could create an adapter/facade class for each the expose a common interface. eg:
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
public class FieldWrapper
{
private Action<double> _levelSetter;
private Func<double> _levelGetter;
private Action<bool> _enableSetter;
private Func<bool> _enableGetter;
public double level { get { return _levelGetter(); } set { _levelSetter(value); }}
public bool isEnabled { get { return _enableGetter(); } set { _enableSetter(value); }}
internal FieldWrapper(Func<double> levelGetter, Action<double> levelSetter, Func<bool> enableGetter, Action<bool> enableSetter)
{
_levelGetter = levelGetter;
_levelSetter = levelSetter;
_enableGetter = enableGetter;
_enableSetter = enableSetter;
}
}
abstract class FancyWrapper
{
public FieldWrapper[] Fields { get; protected set; }
}
class Fancy1Wrapper : FancyWrapper
{
private Fancy1 _fancy1;
public Fancy1Wrapper(Fancy1 fancy1)
{
_fancy1 = fancy1;
this.Fields = new[] { new FieldWrapper(() => fancy1.level1, level => _fancy1.level1 = level, () => _fancy1.isEnable1, enable => _fancy1.isEnable1 = enable),
new FieldWrapper(() => fancy1.level2, level => _fancy1.level2 = level, () => _fancy1.isEnable2, enable => _fancy1.isEnable2 = enable), };
}
}
Or you could invest 5 minutes learning data structures. Consider following example:
var levels = new Dictionary<int, bool>
{
{1, true},
{2, false}
};
if (levels[1])
{
//will run, because level 1 is true
}
if (levels[2])
{
//will not run, because level 2 is false
}
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because dictionary does not contain entry for key 3
}
levels.Add(3, false);
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because level 3 is false
}
levels[3] = true;
if (levels.ContainsKey(3) && levels[3])
{
//will run, because level 3 is true
}
That may seem like what you want, but it really isn't. It is extremely awkward on any number of levels. More specifically, pointers are generally rather "Un-C#-like" and having to know about these numbers defeats the point of having separate classes to begin with.
Think closely about what you want to accomplish. If you're having problems translating it into code, we're here to help. :)

Vector math dimension consistency check at compile-time

I am creating a linear algebra library in C#, and I would like to force dimension inconsistency errors up to compile-time. I've implemented a similar solution to this, where the trait I use is a class that uniquely maps to an integer. The problem is for every possible size I would like my Vectors to have, I would need to create a class with a unique name to represent it.
Here is an example of that implementation:
public class Vector<T> where T: ISize, new()
{
static readonly T size = new T();
List<double> values;
public Vector(List<double> values)
{
if (values.Count != size.Size)
throw new IndexOutOfRangeException();
this.values = new List<double>(values);
}
public double Get(int index)
{
return values[index];
}
public Vector<T> Add(Vector<T> other)
{
var vv = new List<double>();
for (int ii = 0; ii < size.Size; ++ii)
vv.Add(other.Get(ii) + this.values[ii]);
return new Vector<T>(vv);
}
}
public interface ISize
{
int Size { get; }
}
public class S1 : ISize
{
public int Size
{
get { return 1; }
}
}
public class S2 : ISize
{
public int Size
{
get { return 2; }
}
}
And here's an example of its usage:
class Program
{
static void Main(string[] args)
{
var v1 = new Vector<S2>(new List<double>() { 1, 2 });
var v2 = new Vector<S2>(new List<double>() { 10, -4 });
var z1 = new Vector<S1>(new List<double>() { 10 });
// works
var v3 = v1.Add(v2);
// complie-time error
var z2 = z1.Add(v1);
}
}
This works quite well for my purposes, except for the fact that I would need to create a different implementation of ISize for every possible Vector size. Is there any way for me to implement the Vector class that would allow me to get around this problem?
In order to get a compile-time error, you need to have different types. C# does not have a concept that let's you define a type parameter that itself takes a kind of value parameters - which is what you would need to do this.
Therefore, I don't think what you are asking is possible.
I think there might be a way to make unique types for family of vector instances using anonymous types, but that's going to be quirky and I don't think it would provide the type safety that you want.
C++ has such a concept in templates (so it's not unreasonable), just not possible in C#.
You can create a single N-dimentional Vector class with compile time type checking, but it's pretty messy. What we're creating here is LISP style linked-lists, but through generic type arguments rather than purely out of object references via fields.
public interface IVector
{
double Value { get; }
IVector Tail { get; }
}
public class Vector<T> : IVector
where T : IVector
{
internal Vector(double value, T tail)
{
Value = value;
Tail = tail;
}
public double Value { get; private set; }
public T Tail { get; private set; }
public Vector<Vector<T>> Add(double value)
{
return new Vector<Vector<T>>(value, this);
}
}
internal class EmptyVector : IVector
{
public double Value
{
get { throw new NotImplementedException(); }
}
public IVector Tail
{
get { return null; }
}
}
public static class Vector
{
public static readonly Vector<IVector> Empty = new Vector<IVector>(
0, new EmptyVector());
public static IEnumerable<double> AllValues(this IVector vector)
{
IVector current = vector;
while (current != Vector.Empty && current != null)
{
yield return current.Value;
current = current.Tail;
};
}
}
This allows us to write:
var v1 = Vector.Empty.Add(1).Add(2);
var v2 = Vector.Empty.Add(10).Add(-4);
var z1 = Vector.Empty.Add(10);
v1 = v2;//works, as they are the same type
z1 = v2;//fails, as they aren't the same type, since they're a different size
This allows allows you to write a method that accepts a vector of a particular size. It's not convenient, and it doesn't scale, but it works. If you want, say, a 3D vector as a parameter, you can write:
public static void Foo(Vector<Vector<Vector<IVector>>> vector)
{
var first = vector.Value;
var second = vector.Tail.Value;
var third = vector.Tail.Tail.Value;
}

Selecting Items using a HashSet C#

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.

Implementing Linq Distinct in c#

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.

Categories