Словарь С#, который использует неупорядоченную пару в качестве ключа?
Я пытаюсь создать словарь C#, который использует 9X_dictionary неупорядоченную пару индексов в качестве 9X_map ключа.
Например:
exampleDictionary[new UnorderedPair(x,y)]
и exampleDictionary[new UnorderedPair(y,x)]
должны возвращать одно 9X_csharp и то же значение.
Есть ли способ создать 9X_dict пользовательскую неупорядоченную коллекцию, кроме 9X_unordered-set использования HashSet? Или какой-то способ 9X_unordered-set создать неупорядоченный кортеж?
This question похож на 9X_c#-language то, что я пытаюсь сделать, за исключением 9X_unity3d C#, а не python.
Ответ #1
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Если тип не ваш или вы не можете или не хотите его изменять, обратитесь к Theodor Zoulias's answer
В противном случае, предполагая, что UnorderedPair
является 9X_c# вашим собственным классом, вы можете изменить 9X_c#-language то, что вы можете сделать, например.
[Serializable] public class UnorderedPair : IEquatable> { public T X; public T Y; public UnorderedPair() { } public UnorderedPair(T x, T y) { X = x; Y = y; } public bool Equals(UnorderedPair other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } // For equality simply include the swapped check return X.Equals(other.X) && Y.Equals(other.Y) || X.Equals(other.Y) && Y.Equals(other.X); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } return Equals((UnorderedPair)obj); } public override int GetHashCode() { // and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^ var hashX = X == null ? 0 : X.GetHashCode(); var hashY = Y == null ? 0 : Y.GetHashCode(); return HashCode.Combine(Math.Min(hashX,hashY), Math.Max(hashX,hashY)); } public static bool operator ==(UnorderedPair left, UnorderedPair right) { return Equals(left, right); } public static bool operator !=(UnorderedPair left, UnorderedPair right) { return !Equals(left, right); } }
а затем, например,
var testDict = new Dictionary, string>(); testDict.Add(new UnorderedPair(1,2), "Hello World!"); Console.WriteLine(testDict[new UnorderedPair(2,1)]);
Согласно 9X_unordered предложению Джодрелла в комментариях, вы 9X_unordered-set даже можете сделать типы взаимозаменяемыми 9X_dict - не уверен, что это когда-либо понадобится 9X_unity-game-engine - но таким образом вы даже можете иметь 9X_csharp пару разных типов:
[Serializable] public class UnorderedPair : IEquatable> { public TX X; public TY Y; public UnorderedPair() { } public UnorderedPair(TX x, TY y) { X = x; Y = y; } public UnorderedPair(TY y, TX x) { X = x; Y = y; } public override int GetHashCode() { // and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^ var hashX = X == null ? 0 : X.GetHashCode(); var hashY = Y == null ? 0 : Y.GetHashCode(); var combine = HashCode.Combine(Math.Min(hashX, hashY), Math.Max(hashX, hashY)); return combine; } public bool Equals(UnorderedPair other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } if (typeof(TX) != typeof(TY)) { return EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y); } return EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y) || X.Equals(other.Y) && Y.Equals(other.X); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } return obj switch { UnorderedPair other => Equals(other), UnorderedPair otherSwapped => Equals(otherSwapped), _ => false }; } public static bool operator ==(UnorderedPair left, UnorderedPair right) { return Equals(left, right); } public static bool operator !=(UnorderedPair left, UnorderedPair right) { return !Equals(left, right); } public static implicit operator UnorderedPair(UnorderedPair pair) { return new UnorderedPair(pair.Y, pair.X); } }
и
var testDict = new Dictionary, string>(); testDict.Add(new UnorderedPair(1,2.5), "Hello World!"); Console.WriteLine(testDict[new UnorderedPair(2.5,1)]);
(.NET Fiddle for both)
Ответ #2
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Вы можете написать собственную реализацию 9X_unity5 IEqualityComparer>
и передать ее в качестве аргумента конструктору 9X_unordered-set вашего Dictionary, TValue>
. Таким образом, вам не придется 9X_visual-c# изменять тип UnorderedPair
, переопределяя его методы 9X_c-sharp Equals
и GetHashCode
. Ниже приведен пример такого компаратора 9X_csharp для структуры ValueTuple
, где T1
и T2
имеют один и тот 9X_unity2d же тип:
class UnorderedValueTupleEqualityComparer : IEqualityComparer<(T, T)> { private readonly IEqualityComparer _comparer; public UnorderedValueTupleEqualityComparer(IEqualityComparer comparer = default) { _comparer = comparer ?? EqualityComparer.Default; } public bool Equals((T, T) x, (T, T) y) { if (_comparer.Equals(x.Item1, y.Item1) && _comparer.Equals(x.Item2, y.Item2)) return true; if (_comparer.Equals(x.Item1, y.Item2) && _comparer.Equals(x.Item2, y.Item1)) return true; return false; } public int GetHashCode((T, T) obj) { int h1 = _comparer.GetHashCode(obj.Item1); int h2 = _comparer.GetHashCode(obj.Item2); if (h1 > h2) (h1, h2) = (h2, h1); return HashCode.Combine(h1, h2); } }
Пример использования:
Dictionary<(int, int), string> dictionary = new( new UnorderedValueTupleEqualityComparer());
Ответ #3
Ответ на вопрос: Словарь С#, который использует неупорядоченную пару в качестве ключа?
Вдохновленный @derHugo's answer и моими комментариями к нему,
Fiddle here
Общая 9X_dictionary реализация,
#nullable enable public class UnorderedPair : IEquatable> { private static IEqualityComparer comparer = EqualityComparer.Default; public T X { get; } public T Y { get; } public UnorderedPair(T x, T y) { X = x; Y = y; } public bool Equals(UnorderedPair? other) { if(other is null) { return false; } if (ReferenceEquals(this, other)) { return true; } // For equality simply include the swapped check return comparer.Equals(X, other.X) && comparer.Equals(Y, other.Y) || comparer.Equals(X, other.Y) && comparer.Equals(Y, other.X); } public override bool Equals(object? obj) { return Equals(obj as UnorderedPair); } public override int GetHashCode() { unchecked { return (X is null ? 0 : comparer.GetHashCode(X)) + (Y is null ? 0 : comparer.GetHashCode(Y)); } } public static bool operator ==(UnorderedPair? left, UnorderedPair? right) { return Equals(left, right); } public static bool operator !=(UnorderedPair? left, UnorderedPair? right) { return !Equals(left, right); } } #nullable disable
-
3
-
3
-
7
-
2
-
2
-
5
-
3
-
3
-
3
-
2
-
8
-
2
-
5
-
2
-
4
-
3
-
2
-
2
-
3
-
1
-
6
-
2
-
2
-
2
-
5
-
3
-
4
-
2
-
3
-
3
-
7
-
5
-
3
-
5
-
3
-
2
-
1
-
2
-
5
-
2
-
2
-
4
-
2
-
2
-
3
-
2
-
2
-
3
-
3
-
3