2012-02-24 29 views
11

Zastanawiam się, czy był wbudowany typ w C#, który był jak "słownik", ale gdzie zarówno TKey, jak i TValue musiały być unikalne.C# typ słownika z unikatowymi kluczami i wartościami

Na przykład ::

d.Add(1, "1"); 
d.Add(2, "1"); // This would not be OK because "1" has already been used as a value. 

wiem, że to jest trochę egzotyczne, ale wydaje się, że skoro istnieje około miliarda rodzaje kolekcji w plc może istnieć. Jakieś pomysły?

+3

Wprowadź wartość części klucza. –

+3

Nie ma takiej klasy w .NET Framework. Ale możesz łatwo skonstruować jeden ze Słownika i HashSet lub dwa Słowniki. – dtb

+1

@Robert Harvey: jeśli to zrobi, nie może już "d [1]", co pokonuje cel Słownika. Może również użyć HashSet <> –

Odpowiedz

13

Co powiesz na posiadanie Słownika i HashSet/pomocniczego Reverse Dictionary - rozwiąże to problem i będzie działał lepiej niż sprawdza pojedynczy słownik.

Coś takiego, pakowane jako klasy:

HashSet<string> secondary = new HashSet<string>(/*StringComparer.InvariantCultureIgnoreCase*/); 
Dictionary<int, string>dictionary = new Dictionary<int, string>(); 
object syncer = new object(); 

public override void Add(int key, string value) 
{ 
    lock(syncer) 
    { 
    if(dictionary.ContainsKey(key)) 
    { 
     throw new Exception("Key already exists"); 
    } 

    if(secondary.Add(value) 
    { 
     throw new Exception("Value already exists"); 
    } 
    dictionary.Add(key, value); 
    } 
} 
+1

A następnie zawiń to w niestandardową klasę – Jason

+0

@Jason Jeśli potrzebujesz tego więcej niż 1 raz - zdecydowanie tak! –

+1

@OlegDok Jeśli musisz przeczytać kod więcej niż 1 raz - zdecydowanie tak! (I zawsze powinieneś zawsze oczekiwać, że będziesz musiał ponownie przeczytać ten kod pewnego dnia.) –

0

Jest projekt znajduje here że ma typ takiego. Nazywa się PairDictionary i działa całkiem nieźle. To nie najlepsza odpowiedź, ale dla każdego, kto potrzebuje tej niestandardowej klasy.

+0

"ParaDictionary" 'jest okropnym rozwiązaniem, wewnętrznie działa z listami, a każda operacja jest operacją O (n) w przeciwieństwie do O (1) w słowniku _real_ --- --- [źródło] (http://curations.codeplex.com/SourceControl/ ostatnie # Curations/PairDictionary.cs) – t3chb0t

+0

@ t3chb0t Skupiasz się na implementacji, a nie na API.Tak, nie jest najlepszy, ale jego użycie jest ważne. który jest częścią jakiegoś kodu open source, powinieneś przesłać poprawkę poprawiającą jej funkcjonalność :) –

0

Rozwiązałem ten problem, przechowując dane jako Dictionary<TKey, HashSet<TValue>>. Możesz zastąpić HashSet innym Słownikiem, jeśli chcesz uzyskać wartość, która ma 2 klucze podstawowe.

Dictionary<int, HashSet<int>> _myUniquePairOfIntegerKeys; 
// OR 
Dictionary<string, Dictionary<string, bool>> _myUniquePairOfStringKeysWithABooleanValue; 
1

Dla wewnętrznych puppies napisałem BiDictionary. Nie jest to kuloodporne, ponieważ nie ujawniam go użytkownikowi, więc działa dobrze dla mnie. Pozwala mi uzyskać klucz, tak jak ja potrzebuję.

Konieczne jest, aby KeyPair<,> móc wdrożyć metodę IEnumerable<,>, a tym samym Add, abyśmy mogli korzystać z inicjalizatora obiektu.

internal class KeyPair<TKey1, TKey2> 
{ 
    public TKey1 Key1 { get; set; } 
    public TKey2 Key2 { get; set; } 
} 

Jest to główny klasy jako dynamiczny obiektu tak, że przy pobieraniu wartości możemy użyć nazwy klawiszy od niego:

internal class BiDictionary<TKey1, TKey2> : DynamicObject, IEnumerable<KeyPair<TKey1, TKey2>> 
{ 
    private readonly Dictionary<TKey1, TKey2> _K1K2 = new Dictionary<TKey1, TKey2>(); 
    private readonly Dictionary<TKey2, TKey1> _K2K1 = new Dictionary<TKey2, TKey1>(); 

    private readonly string _key1Name; 
    private readonly string _key2Name; 

    public BiDictionary(string key1Name, string key2Name) 
    { 
     _key1Name = key1Name; 
     _key2Name = key2Name; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (binder.Name == _key1Name) 
     { 
      result = _K1K2; 
      return true; 
     } 

     if (binder.Name == _key2Name) 
     { 
      result = _K2K1; 
      return true; 
     } 

     result = null; 
     return false; 
    } 

    public void Add(TKey1 key1, TKey2 key2) 
    { 
     _K1K2.Add(key1, key2); 
     _K2K1.Add(key2, key1); 
    } 

    public IEnumerator<KeyPair<TKey1, TKey2>> GetEnumerator() 
    { 
     return _K1K2.Zip(_K2K1, (d1, d2) => new KeyPair<TKey1, TKey2> 
     { 
      Key1 = d1.Key, 
      Key2 = d2.Key 
     }).GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Przykład:

dynamic bidic = new BiDictionary<string, string>("Key1", "Key2") 
{ 
    { "foo", "bar" }, 
    { "baz", "qux" } 
}; 
var bar = bidic.Key1["foo"]; 
var foo = bidic.Key2["bar"]; 

Mogą zsynchronizować, jeśli zmodyfikujesz dowolny ze słowników na zewnątrz. W tym celu używam ObservableDictionary, dzięki czemu mogę zaktualizować drugi, jeśli się zmieni, ale dla uproszczenia usunąłem tę część kodu, aby zdemontować główną logikę.

Powiązane problemy