C # iki yönlü / çift yönlü Sözlük?


90

Kelimeleri bir sözlükte şu şekilde saklamak istiyorum:

Kelime kodunu kelimeye göre alabilirim : dict["SomeWord"]-> 123ve kelime kodu kelime alabilirim: dict[123]->"SomeWord"

Bu gerçek mi? Tabii bir yolu iki sözlükleri yapmanın: Dictionary<string,int>ve Dictionary<int,string>ancak başka bir yolu var mı?


2
Her iki yoldan O (1) erişimi sağlayan standart (.NET 4 itibariyle) veri türü yoktur ... AFAIK :)

Ayrıca, çift yönlü bir harita (anahtar kelime?), Çok yönlü bir harita olmadığı sürece ek kısıtlamalar

Yanıtlar:


110

İstediğinizi yapmanıza izin veren hızlı birkaç ders yazdım. Muhtemelen daha fazla özellikle genişletmeniz gerekir, ancak bu iyi bir başlangıç ​​noktasıdır.

Kodun kullanımı şu şekildedir:

var map = new Map<int, string>();

map.Add(42, "Hello");

Console.WriteLine(map.Forward[42]);
// Outputs "Hello"

Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42

İşte tanım:

public class Map<T1, T2>
{
    private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        this.Forward = new Indexer<T1, T2>(_forward);
        this.Reverse = new Indexer<T2, T1>(_reverse);
    }

    public class Indexer<T3, T4>
    {
        private Dictionary<T3, T4> _dictionary;
        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }
        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }
    }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }
}

2
@ Pedro77 - Şimdi var. ;-)
Enigmativity

2
@ Pedro77 - Sınıfımın yeni "harita" çözümü olduğunu söyleyerek küstah davranıyordum.
Enigmativity

12
Bu, istisnalarda sınıf değişmezlerini korumaz. Sizi kısmen eklenmiş bir çiftle bırakarak _forward.Addbaşarılı olmak ve _reverse.Addbaşarısız olmak mümkündür .

5
@hvd - Dediğim gibi - hızlı bir şekilde bir araya getirilmiş bir ders.
Enigmativity

3
@AaA ForwardKendi (sahip olduğu private set;) sözlük özelliğini değiştirmiyor , ancak sözlüğe ileten Indexer sınıfının Indexer özelliği aracılığıyla bu sözlükteki değeri değiştiriyor. public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } }Yani bu ileri / geri aramasını bozuyor.
Jeroen van Langen

27

Ne yazık ki, her yön için bir tane olmak üzere iki sözlüğe ihtiyacınız var. Ancak, ters sözlüğü LINQ kullanarak kolayca alabilirsiniz:

Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);

11

İlklendirmeler ve İçerir yöntemi eklenerek Enigmativity kodunda genişletildi.

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
    private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    public void Remove(T1 t1)
    {
        T2 revKey = Forward[t1];
        _forward.Remove(t1);
        _reverse.Remove(revKey);
    }
    
    public void Remove(T2 t2)
    {
        T1 forwardKey = Reverse[t2];
        _reverse.Remove(t2);
        _forward.Remove(forwardKey);
    }

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

    public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
    {
        return _forward.GetEnumerator();
    }

    public class Indexer<T3, T4>
    {
        private readonly Dictionary<T3, T4> _dictionary;

        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }

        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }

        public bool Contains(T3 key)
        {
            return _dictionary.ContainsKey(key);
        }
    }
}

İşte bir kullanım örneği, geçerli parantezleri kontrol edin

public static class ValidParenthesisExt
{
    private static readonly Map<char, char>
        _parenthesis = new Map<char, char>
        {
            {'(', ')'},
            {'{', '}'},
            {'[', ']'}
        };

    public static bool IsValidParenthesis(this string input)
    {
        var stack = new Stack<char>();
        foreach (var c in input)
        {
            if (_parenthesis.Forward.Contains(c))
                stack.Push(c);
            else
            {
                if (stack.Count == 0) return false;
                if (_parenthesis.Reverse[c] != stack.Pop())
                    return false;
            }
        }
        return stack.Count == 0;
    }
}

7

Başkalarının söylediği gibi iki sözlük kullanabilirsiniz, ancak her ikisi de TKeyve TValueaynı türdeyse (ve çalışma zamanı değer alanlarının ayrık olduğu biliniyorsa), her anahtar için iki giriş oluşturarak aynı sözlüğü kullanabilirsiniz. / değer eşleştirme:

dict["SomeWord"]= "123" ve dict["123"]="SomeWord"

Bu şekilde, her iki arama türü için tek bir sözlük kullanılabilir.


3
Evet, bu yaklaşım soruda onaylandı :)

3
Bu, aynı değerin hem "anahtarlarda" hem de "değerlerde" bulunma olasılığını göz ardı eder. o zaman bu çözümde çelişir.
user1028741

1
@ user1028741 Kabul edildi, ancak örnekte "aynı türden" değil "farklı bir tür anlamına geldiği anlaşılıyor
Hutch

Bu, gelecekte beklenmeyen sonuçlara neden olabilir ve ardından kod yeniden düzenlemeden geçer. Örneğin, daha sonra sol ve sağ taraflar üst üste gelmeye başlar. Performansta hiçbir şeye yakın değildir.
Vinigas

6

Ne oluyor, kendi versiyonumu karışıma ekleyeceğim:

public class BijectiveDictionary<TKey, TValue> 
{
    private EqualityComparer<TKey> _keyComparer;
    private Dictionary<TKey, ISet<TValue>> _forwardLookup;
    private EqualityComparer<TValue> _valueComparer;
    private Dictionary<TValue, ISet<TKey>> _reverseLookup;             

    public BijectiveDictionary()
        : this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    {
    }

    public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
        : this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    {
    }

    public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
    {
        _keyComparer = keyComparer;
        _forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer);            
        _valueComparer = valueComparer;
        _reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer);            
    }

    public void Add(TKey key, TValue value)
    {
        AddForward(key, value);
        AddReverse(key, value);
    }

    public void AddForward(TKey key, TValue value)
    {
        ISet<TValue> values;
        if (!_forwardLookup.TryGetValue(key, out values))
        {
            values = new HashSet<TValue>(_valueComparer);
            _forwardLookup.Add(key, values);
        }
        values.Add(value);
    }

    public void AddReverse(TKey key, TValue value) 
    {
        ISet<TKey> keys;
        if (!_reverseLookup.TryGetValue(value, out keys))
        {
            keys = new HashSet<TKey>(_keyComparer);
            _reverseLookup.Add(value, keys);
        }
        keys.Add(key);
    }

    public bool TryGetReverse(TValue value, out ISet<TKey> keys)
    {
        return _reverseLookup.TryGetValue(value, out keys);
    }

    public ISet<TKey> GetReverse(TValue value)
    {
        ISet<TKey> keys;
        TryGetReverse(value, out keys);
        return keys;
    }

    public bool ContainsForward(TKey key)
    {
        return _forwardLookup.ContainsKey(key);
    }

    public bool TryGetForward(TKey key, out ISet<TValue> values)
    {
        return _forwardLookup.TryGetValue(key, out values);
    }

    public ISet<TValue> GetForward(TKey key)
    {
        ISet<TValue> values;
        TryGetForward(key, out values);
        return values;
    }

    public bool ContainsReverse(TValue value)
    {
        return _reverseLookup.ContainsKey(value);
    }

    public void Clear()
    {
        _forwardLookup.Clear();
        _reverseLookup.Clear();
    }
}

Ona biraz veri ekleyin:

var lookup = new BijectiveDictionary<int, int>();

lookup.Add(1, 2);
lookup.Add(1, 3);
lookup.Add(1, 4);
lookup.Add(1, 5);

lookup.Add(6, 2);
lookup.Add(6, 8);
lookup.Add(6, 9);
lookup.Add(6, 10);

Ve sonra aramayı yapın:

lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6

Bunun 1: N'yi desteklemesini seviyorum
Sebastian

@Sebastian, IEnumerable <KeyValuePair <TKey, TValue >> ekleyebilirsiniz.
Ostati

4

Numaralandırma kullanmasına rağmen bu uzantı yöntemini kullanabilirsiniz ve bu nedenle büyük veri kümeleri için yeterli performans göstermeyebilir. Verimlilik konusunda endişeleniyorsanız, iki sözlüğe ihtiyacınız var. İki sözlüğü tek bir sınıfa ayırmak istiyorsanız, bu soru için kabul edilen cevaba bakın: C # 'da Çift Yönlü 1'e 1 Sözlük

public static class IDictionaryExtensions
{
    public static TKey FindKeyByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        foreach (KeyValuePair<TKey, TValue> pair in dictionary)
            if (value.Equals(pair.Value)) return pair.Key;

        throw new Exception("the value is not found in the dictionary");
    }
}

8
Bu çift yönlü bir sözlük olmasına rağmen, Değerin getirilmesi bir O (1) işlemi olması gereken bir O (n) işlemidir. Bu, küçük veri kümeleri için önemli olmayabilir, ancak büyük veri kümeleri ile çalışırken performans sorunlarına neden olabilir. Uzayda performans için en iyi cevap, verileri tersine çevrilmiş iki sözlük kullanmak olacaktır.
Tom

@TomA Tom ile tamamen aynı fikirdeyim, gerçek bir çift yönlü sözlüğe ihtiyaç duyduğunuz tek durum, 100.000, 1 milyondan fazla girişiniz olduğunda, onu taramaktan daha az her şey etkili bir NOOP'dur.
Chris Marisic

Durumum için bu çözümü seviyorum (küçük dikte boyutları) çünkü hala koleksiyon başlatıcıları kullanabiliyorum. Kabul edilen yanıtta <A, B> haritası Koleksiyon başlatıcılarda kullanılabileceğini sanmıyorum.
CVertex

@ChrisMarisic, bunu açıklamak tuhaf görünüyor. Bu arama sıkı bir döngüde çağrılsaydı, bahse girerim, 500'den az giriş olsa bile acıyı hissedersiniz. Aynı zamanda bir karşılaştırma testinin maliyetine de bağlıdır. Yorumunuz gibi kapsamlı ifadelerin yardımcı olacağını sanmıyorum.
Lee Campbell

@LeeCampbell benim kapsamlı ifadelerim, ölçülebilir ve profilli gerçeklikte olduğu gibi gerçek gerçeklikteki deneyime dayanmaktadır. Bir sözlüğün anahtarı olarak karmaşık bir tür kullanmak istiyorsanız, bu sizin probleminiz değil.
Chris Marisic

1

Sözlük

İşte her yanıtta sevdiğim şeyin bir karışımı. UygularIEnumerableÖrnekte görebileceğiniz gibi, koleksiyon başlatıcıyı kullanabilmesi için .

Kullanım Kısıtlaması:

  • Farklı veri türleri kullanıyorsunuz. (yani,T1T2 )

Kod:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        Bictionary<string, int> bictionary = 
            new Bictionary<string,int>() {
                { "a",1 }, 
                { "b",2 }, 
                { "c",3 } 
            };

        // test forward lookup
        Console.WriteLine(bictionary["b"]);
        // test forward lookup error
        //Console.WriteLine(bictionary["d"]);
        // test reverse lookup
        Console.WriteLine(bictionary[3]); 
        // test reverse lookup error (throws same error as forward lookup does)
        Console.WriteLine(bictionary[4]); 
    }
}

public class Bictionary<T1, T2> : Dictionary<T1, T2>
{
    public T1 this[T2 index]
    {
        get
        {
            if(!this.Any(x => x.Value.Equals(index)))
               throw new System.Collections.Generic.KeyNotFoundException();
            return this.First(x => x.Value.Equals(index)).Key;
        }
    }
}

Vaktini boşa harcamak:

https://dotnetfiddle.net/mTNEuw


Çok zarif çözüm! Bunun cesaretini biraz daha açıklayabilir misin? Bictionary<string, string>Tüm dizeler benzersiz olsa bile yapamayacağın konusunda haklı mıyım ?
Marcus Mangelsdorf

@ Merlin2001, Bu doğru. Daha doğrusu, bununla ileriye dönük arama yapamazsınız. Bunun üstesinden nasıl geleceğimi düşünmem gerek. Derler ama her zaman ters indeksleyiciyi ilk ne zaman bulur T1 == T2, bu yüzden ileriye dönük aramalar başarısız olur. Ayrıca, varsayılan indeksleyiciyi geçersiz kılamıyorum çünkü o zaman arama aramaları belirsiz olacaktır. Bu kısıtlamayı ekledim ve öncekini kaldırdım, çünkü değerlerinin değerleri ile T1çakışabilir T2.
Toddmo

10
Ters tarafta oldukça ciddi bir performans sorunu var; sözlük, O (n) performans isabeti ile iki kez aranır; ikinci bir sözlük kullanmak çok daha hızlı olurdu ve tür kısıtlamasını kaldırırdı.
Steve Cooper

@SteveCooper, belki performans isabetinden kurtulmak için onu bir içine sararak tryve istisnaları KeyNotFoundExceptions.
Toddmo

4
@toddmo bu şekilde hızı ikiye katlayabilirsiniz. Daha büyük sorun, hem .First hem de .Any bir seferde bir öğeyi arayıp, her birini test etmesidir. Bu nedenle, 1.000.000 öğe listesinin test edilmesi, 1 öğeli listeden 1.000.000 kat daha uzun sürer. Sözlükler çok daha hızlıdır ve siz daha fazla öğe ekledikçe yavaşlamaz, bu nedenle ikinci bir ters sözlük, büyük listelerde epik miktarda zaman kazandırır. Alakalı olmayabilir, ancak test sırasında küçük miktarlarda veriyle iyi olabilecek türden bir şeydir ve daha sonra ciddi verilerle gerçek bir sunucudaki performansı öldürür.
Steve Cooper

1

Bu eski bir sorun ancak birinin yararlı bulması durumunda iki uzantı yöntemi eklemek istedim. İkincisi o kadar kullanışlı değildir, ancak bire bir sözlüklerin desteklenmesi gerekiyorsa bir başlangıç ​​noktası sağlar.

    public static Dictionary<VALUE,KEY> Inverse<KEY,VALUE>(this Dictionary<KEY,VALUE> dictionary)
    {
        if (dictionary==null || dictionary.Count == 0) { return null; }

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach(KeyValuePair<KEY,VALUE> entry in dictionary)
        {
            result.Add(entry.Value, entry.Key);
        }

        return result;
    }

    public static Dictionary<VALUE, KEY> SafeInverse<KEY, VALUE>(this Dictionary<KEY, VALUE> dictionary)
    {
        if (dictionary == null || dictionary.Count == 0) { return null; }

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach (KeyValuePair<KEY, VALUE> entry in dictionary)
        {
            if (result.ContainsKey(entry.Value)) { continue; }

            result.Add(entry.Value, entry.Key);
        }

        return result;
    }

1

Comparers'ı ileri almak ve geri almak için ek bir kurucu ile Xavier John'un cevabının değiştirilmiş bir versiyonu. Bu, örneğin, büyük / küçük harfe duyarlı olmayan anahtarları destekler. İleri ve geri Dictionary oluşturuculara başka argümanlar iletmek için gerekirse başka kurucular eklenebilir.

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
    private readonly Dictionary<T1, T2> _forward;
    private readonly Dictionary<T2, T1> _reverse;

    /// <summary>
    /// Constructor that uses the default comparers for the keys in each direction.
    /// </summary>
    public Map()
        : this(null, null)
    {
    }

    /// <summary>
    /// Constructor that defines the comparers to use when comparing keys in each direction.
    /// </summary>
    /// <param name="t1Comparer">Comparer for the keys of type T1.</param>
    /// <param name="t2Comparer">Comparer for the keys of type T2.</param>
    /// <remarks>Pass null to use the default comparer.</remarks>
    public Map(IEqualityComparer<T1> t1Comparer, IEqualityComparer<T2> t2Comparer)
    {
        _forward = new Dictionary<T1, T2>(t1Comparer);
        _reverse = new Dictionary<T2, T1>(t2Comparer);
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    }

    // Remainder is the same as Xavier John's answer:
    // https://stackoverflow.com/a/41907561/216440
    ...
}

Büyük / küçük harfe duyarlı olmayan bir anahtarla kullanım örneği:

Map<int, string> categories = 
new Map<int, string>(null, StringComparer.CurrentCultureIgnoreCase)
{
    { 1, "Bedroom Furniture" },
    { 2, "Dining Furniture" },
    { 3, "Outdoor Furniture" }, 
    { 4, "Kitchen Appliances" }
};

int categoryId = 3;
Console.WriteLine("Description for category ID {0}: '{1}'", 
    categoryId, categories.Forward[categoryId]);

string categoryDescription = "DINING FURNITURE";
Console.WriteLine("Category ID for description '{0}': {1}", 
    categoryDescription, categories.Reverse[categoryDescription]);

categoryDescription = "outdoor furniture";
Console.WriteLine("Category ID for description '{0}': {1}", 
    categoryDescription, categories.Reverse[categoryDescription]);

// Results:
/*
Description for category ID 3: 'Outdoor Furniture'
Category ID for description 'DINING FURNITURE': 2
Category ID for description 'outdoor furniture': 3
*/

1

İşte kodum. Tohumlanmış yapıcılar dışında her şey O (1) 'dir.

using System.Collections.Generic;
using System.Linq;

public class TwoWayDictionary<T1, T2>
{
    Dictionary<T1, T2> _Forwards = new Dictionary<T1, T2>();
    Dictionary<T2, T1> _Backwards = new Dictionary<T2, T1>();

    public IReadOnlyDictionary<T1, T2> Forwards => _Forwards;
    public IReadOnlyDictionary<T2, T1> Backwards => _Backwards;

    public IEnumerable<T1> Set1 => Forwards.Keys;
    public IEnumerable<T2> Set2 => Backwards.Keys;


    public TwoWayDictionary()
    {
        _Forwards = new Dictionary<T1, T2>();
        _Backwards = new Dictionary<T2, T1>();
    }

    public TwoWayDictionary(int capacity)
    {
        _Forwards = new Dictionary<T1, T2>(capacity);
        _Backwards = new Dictionary<T2, T1>(capacity);
    }

    public TwoWayDictionary(Dictionary<T1, T2> initial)
    {
        _Forwards = initial;
        _Backwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    }

    public TwoWayDictionary(Dictionary<T2, T1> initial)
    {
        _Backwards = initial;
        _Forwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    }


    public T1 this[T2 index]
    {
        get => _Backwards[index];
        set
        {
            if (_Backwards.TryGetValue(index, out var removeThis))
                _Forwards.Remove(removeThis);

            _Backwards[index] = value;
            _Forwards[value] = index;
        }
    }

    public T2 this[T1 index]
    {
        get => _Forwards[index];
        set
        {
            if (_Forwards.TryGetValue(index, out var removeThis))
                _Backwards.Remove(removeThis);

            _Forwards[index] = value;
            _Backwards[value] = index;
        }
    }

    public int Count => _Forwards.Count;

    public bool Contains(T1 item) => _Forwards.ContainsKey(item);
    public bool Contains(T2 item) => _Backwards.ContainsKey(item);

    public bool Remove(T1 item)
    {
        if (!this.Contains(item))
            return false;

        var t2 = _Forwards[item];

        _Backwards.Remove(t2);
        _Forwards.Remove(item);

        return true;
    }

    public bool Remove(T2 item)
    {
        if (!this.Contains(item))
            return false;

        var t1 = _Backwards[item];

        _Forwards.Remove(t1);
        _Backwards.Remove(item);

        return true;
    }

    public void Clear()
    {
        _Forwards.Clear();
        _Backwards.Clear();
    }
}

Anahtar ve değer türlerinin aynı olduğu mevcut bir sözlüğe geçerseniz kurucunun nasıl davranacağını merak ediyorum. Geriye mi yoksa ileriye doğru olanı kullanıp kullanmamayı nasıl çözecek?
Colm Bhandal

0

Aşağıdaki kapsülleme sınıfı, 1 sözlük örneği üzerinden linq (IEnumerable Extensions) kullanır.

public class TwoWayDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> dict;
    readonly Func<TKey, TValue> GetValueWhereKey;
    readonly Func<TValue, TKey> GetKeyWhereValue;
    readonly bool _mustValueBeUnique = true;

    public TwoWayDictionary()
    {
        this.dict = new Dictionary<TKey, TValue>();
        this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault();
        this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault();
    }

    public TwoWayDictionary(KeyValuePair<TKey, TValue>[] kvps)
        : this()
    {
        this.AddRange(kvps);
    }

    public void AddRange(KeyValuePair<TKey, TValue>[] kvps)
    {
        kvps.ToList().ForEach( kvp => {        
            if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value)))
            {
                dict.Add(kvp.Key, kvp.Value);
            } else {
                throw new InvalidOperationException("Value must be unique");
            }
        });
    }

    public TValue this[TKey key]
    {
        get { return GetValueWhereKey(key); }
    }

    public TKey this[TValue value]
    {
        get { return GetKeyWhereValue(value); }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var dict = new TwoWayDictionary<string, int>(new KeyValuePair<string, int>[] {
            new KeyValuePair<string, int>(".jpeg",100),
            new KeyValuePair<string, int>(".jpg",101),
            new KeyValuePair<string, int>(".txt",102),
            new KeyValuePair<string, int>(".zip",103)
        });


        var r1 = dict[100];
        var r2 = dict[".jpg"];

    }

}

0

Bu, geriye doğru arama için bir indeksleyici kullanır.
Ters arama O (n) ama aynı zamanda iki sözlük kullanmıyor

public sealed class DictionaryDoubleKeyed : Dictionary<UInt32, string>
{   // used UInt32 as the key as it has a perfect hash
    // if most of the lookup is by word then swap
    public void Add(UInt32 ID, string Word)
    {
        if (this.ContainsValue(Word)) throw new ArgumentException();
        base.Add(ID, Word);
    }
    public UInt32 this[string Word]
    {   // this will be O(n)
        get
        {
            return this.FirstOrDefault(x => x.Value == Word).Key;
        }
    } 
}

Örneğin: olası NRE girişi this[string Word]. Ek sorunlar, ortak uygulamalara karşılık gelmeyen değişken isimleridir, kodla tutarsız yorumlar ( UInt16vs UInt32- bu yüzden: yorum kullanmayın!), Çözüm genel değil, ...
BartoszKP

0

İşte önerilenlere alternatif bir çözüm. İç sınıfı kaldırdı ve öğe eklerken / çıkarırken tutarlılığı sağladı

using System.Collections;
using System.Collections.Generic;

public class Map<E, F> : IEnumerable<KeyValuePair<E, F>>
{
    private readonly Dictionary<E, F> _left = new Dictionary<E, F>();
    public IReadOnlyDictionary<E, F> left => this._left;
    private readonly Dictionary<F, E> _right = new Dictionary<F, E>();
    public IReadOnlyDictionary<F, E> right => this._right;

    public void RemoveLeft(E e)
    {
        if (!this.left.ContainsKey(e)) return;
        this._right.Remove(this.left[e]);
        this._left.Remove(e);
    }

    public void RemoveRight(F f)
    {
        if (!this.right.ContainsKey(f)) return;
        this._left.Remove(this.right[f]);
        this._right.Remove(f);
    }

    public int Count()
    {
        return this.left.Count;
    }

    public void Set(E left, F right)
    {
        if (this.left.ContainsKey(left))
        {
            this.RemoveLeft(left);
        }
        if (this.right.ContainsKey(right))
        {
            this.RemoveRight(right);
        }
        this._left.Add(left, right);
        this._right.Add(right, left);
    }


    public IEnumerator<KeyValuePair<E, F>> GetEnumerator()
    {
        return this.left.GetEnumerator();
    }

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


0

Var BijectionDictionaryBu açık kaynak deposunda tür var:

https://github.com/ColmBhandal/CsharpExtras .

Verilen diğer cevaplardan niteliksel olarak çok farklı değil. Bu cevapların çoğu gibi iki sözlük kullanır.

Şimdiye kadarki diğer cevaplara karşı bu sözlüğün yeni olduğuna inanıyorum ki, iki yönlü bir sözlük gibi davranmak yerine, tek yönlü, tanıdık bir sözlük gibi davranması ve ardından dinamik olarak sözlüğü kullanarak sözlüğü çevirmenize izin vermesidir. Ters özelliği. Çevrilmiş nesne referansı sığdır, bu nedenle orijinal referansla aynı çekirdek nesneyi yine de değiştirebilir. Böylece, biri ters çevrilmiş olması dışında aynı nesneye iki referansınız olabilir.

Bu sözlükle ilgili muhtemelen benzersiz olan bir başka şey de, bu deponun altındaki test projesinde onun için yazılmış bazı testlerin olmasıdır. Bizim tarafımızdan pratikte kullanıldı ve şu ana kadar oldukça istikrarlı oldu.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.