Kamus dua arah / dua arah di C #?

90

Saya ingin menyimpan kata-kata dalam kamus dengan cara berikut:

Saya bisa mendapatkan kode kata demi kata: dict["SomeWord"]-> 123dan mendapatkan kode kata demi kata: dict[123]->"SomeWord"

Apakah itu nyata Tentu saja satu cara untuk melakukannya adalah dengan dua kamus: Dictionary<string,int>dan Dictionary<int,string>apakah ada cara lain?

Neir0
sumber
2
Tidak ada tipe data standar (pada .NET 4) yang menyediakan O (1) akses dua arah ... AFAIK :)
Juga bukan bahwa peta dua arah (kata kunci?) Memberlakukan batasan tambahan, kecuali peta multi-dua arah ...

Jawaban:

110

Saya menulis beberapa kelas singkat yang memungkinkan Anda melakukan apa yang Anda inginkan. Anda mungkin perlu memperluasnya dengan lebih banyak fitur, tetapi ini adalah titik awal yang baik.

Penggunaan kode terlihat seperti ini:

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

map.Add(42, "Hello");

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

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

Berikut definisinya:

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; }
}
Enigmativitas
sumber
2
@ Pedro77 - Sekarang. ;-)
Enigmativitas
2
@ Pedro77 - Saya hanya bersikap nakal dengan menyarankan bahwa kelas saya adalah solusi "peta" yang baru.
Enigmativitas
12
Ini tidak mempertahankan invarian kelas pada pengecualian. Mungkin saja untuk _forward.Addberhasil, dan _reverse.Addgagal, meninggalkan Anda dengan pasangan yang ditambahkan sebagian.
5
@hvd - Seperti yang saya katakan - ini adalah kelas yang disatukan dengan cepat.
Enigmativitas
3
@AaA Ini tidak mengubah Forwardproperti kamus itu sendiri (yang dimilikinya private set;), tetapi memodifikasi nilai pada kamus itu melalui properti Pengindeks dari kelas Pengindeks yang meneruskannya ke kamus. public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } }Jadi itu melanggar pencarian maju / mundur.
Jeroen van Langen
27

Sayangnya, Anda membutuhkan dua kamus, satu untuk setiap arah. Namun, Anda bisa dengan mudah mendapatkan kamus terbalik menggunakan LINQ:

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

Memperluas kode Enigmativitas dengan menambahkan metode inisialisasi dan Berisi.

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

Ini adalah kasus penggunaan, periksa tanda kurung yang valid

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;
    }
}
Xavier John
sumber
7

Anda dapat menggunakan dua kamus, seperti yang dikatakan orang lain, tetapi perhatikan juga bahwa jika keduanya TKeydan TValueberjenis sama (dan domain nilai waktu prosesnya diketahui saling terpisah) maka Anda dapat menggunakan kamus yang sama dengan membuat dua entri untuk setiap kunci / nilai pasangan:

dict["SomeWord"]= "123" dan dict["123"]="SomeWord"

Dengan cara ini, satu kamus dapat digunakan untuk kedua jenis pencarian.

zmbq
sumber
3
Ya, pendekatan ini diakui dalam pertanyaan :)
3
Ini mengabaikan kemungkinan nilai yang sama ada di kedua "kunci" dan "nilai". maka itu akan bertentangan dalam solusi ini.
pengguna1028741
1
@ user1028741 Setuju, meskipun dari contoh tampaknya maksudnya "dari jenis yang berbeda" bukan "dari jenis yang sama"
Hutch
Hal ini dapat mengakibatkan hasil yang tidak terduga di masa mendatang, kemudian kode mengalami pemfaktoran ulang. Misal, sisi kiri dan kanan mulai tumpang tindih. Itu hampir tidak menambah kinerja.
Vinigas
6

Apa-apaan ini, saya akan memasukkan versi saya ke dalam campuran:

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

Tambahkan beberapa data ke dalamnya:

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

Lalu lakukan pencarian:

lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6
Ostati
sumber
Saya suka bahwa ini mendukung 1: N
Sebastian
@ Sebastian, Anda dapat menambahkan IEnumerable <KeyValuePair <TKey, TValue >>.
Ostati
4

Anda dapat menggunakan metode ekstensi ini, meskipun menggunakan enumerasi, dan oleh karena itu mungkin tidak berfungsi untuk kumpulan data yang besar. Jika Anda khawatir tentang efisiensi, maka Anda memerlukan dua kamus. Jika Anda ingin menggabungkan dua kamus menjadi satu kelas, lihat jawaban yang diterima untuk pertanyaan ini: Kamus Dua Arah 1 ke 1 di C #

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");
    }
}
moribvndvs
sumber
8
Meskipun ini adalah kamus dua arah, mengambil Nilai adalah operasi O (n) yang seharusnya menjadi operasi O (1). Ini mungkin tidak masalah untuk kumpulan data kecil, tetapi dapat menyebabkan masalah kinerja saat bekerja dengan kumpulan data besar. Jawaban terbaik untuk kinerja di luar angkasa adalah menggunakan dua kamus dengan data terbalik.
Tom
@TomA i sepenuhnya setuju dengan Tom, satu-satunya contoh di mana Anda memerlukan kamus dua arah yang sebenarnya adalah ketika Anda memiliki 100K, 1M + entri, apa pun yang kurang memindai itu secara efektif NOOP.
Chris Marisic
Saya suka solusi ini untuk situasi saya (ukuran dikt kecil) karena saya masih dapat menggunakan penginisialisasi koleksi. Peta <A, B> dalam jawaban yang diterima saya rasa tidak dapat digunakan dalam penginisialisasi koleksi.
CVertex
@ChrisMarisic, sepertinya hal yang aneh untuk diberitakan. Jika pencarian ini dipanggil dalam loop yang ketat, saya yakin, Anda akan merasakan sakitnya bahkan dengan <500 entri. Itu juga tergantung pada biaya tes perbandingan. Saya rasa pernyataan menyeluruh seperti komentar Anda tidak membantu.
Lee Campbell
@LeeCampbell Pernyataan luas saya didasarkan pada pengalaman dalam realitas aktual, seperti dalam realitas yang dapat diukur dan diprofilkan. Jika Anda ingin menggunakan beberapa tipe kompleks sebagai kunci kamus, itu masalah Anda bukan masalah saya.
Chris Marisic
1

Bictionary

Berikut adalah percampuran apa yang saya suka di setiap jawaban. Itu mengimplementasikanIEnumerable sehingga dapat menggunakan penginisialisasi koleksi, seperti yang Anda lihat di contoh.

Batasan Penggunaan:

  • Anda menggunakan tipe data yang berbeda. (yaitu, )T1T2

Kode:

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

Biola:

https://dotnetfiddle.net/mTNEuw

toddmo
sumber
Solusi yang sangat elegan! Bisakah Anda menjelaskan isi perutnya sedikit lebih jauh? Apakah saya benar, bahwa Anda tidak dapat membuat Bictionary<string, string>meskipun semua string itu unik?
Marcus Mangelsdorf
@ Merlin2001, Itu benar. Lebih tepatnya, Anda tidak dapat melakukan pencarian ke depan dengan itu. Saya harus memikirkan bagaimana mengatasinya. Ia mengkompilasi tetapi selalu menemukan pengindeks terbalik terlebih dahulu saat T1 == T2, sehingga pencarian maju gagal. Selain itu, saya tidak dapat mengganti pengindeks default karena panggilan pencarian akan menjadi ambigu. Saya telah menambahkan batasan ini dan menghapus yang sebelumnya, karena nilai T1dapat tumpang tindih dengan nilai T2.
toddmo
10
Ada masalah kinerja yang cukup serius di sisi sebaliknya; kamus dicari dua kali, dengan kinerja O (n) hit; itu akan jauh lebih cepat untuk menggunakan kamus kedua, dan akan menghapus batasan tipe.
Steve Cooper
@SteveCooper, mungkin saya bisa menghilangkan kinerja yang terpukul dengan membungkusnya dalam trydan mengonversi pengecualian menjadi KeyNotFoundExceptions.
toddmo
4
@toddmo Anda mungkin dapat menggandakan kecepatan dengan cara ini. Masalah yang lebih besar adalah bahwa .Pertama dan. Setiap item mencari satu per satu, menguji masing-masing item. Jadi untuk menguji daftar 1.000.000 item membutuhkan 1.000.000 kali lebih lama untuk mencari daripada daftar 1 elemen. Kamus jauh lebih cepat dan tidak melambat saat Anda menambahkan lebih banyak item, jadi kamus terbalik kedua akan menghemat banyak waktu dibandingkan daftar besar. Ini mungkin tidak relevan, tetapi itu adalah hal yang baik dengan sejumlah kecil data selama pengujian, dan kemudian mematikan kinerja di server nyata dengan data yang serius.
Steve Cooper
1

Ini adalah masalah lama tetapi saya ingin menambahkan dua metode ekstensi jika ada yang menganggapnya berguna. Yang kedua tidak begitu berguna tetapi memberikan titik awal jika kamus satu ke satu perlu didukung.

    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;
    }
Felipe Ramos
sumber
1

Versi modifikasi jawaban Xavier John, dengan konstruktor tambahan untuk maju dan mundur Pembanding. Ini akan mendukung kunci case-insensitive, misalnya. Konstruktor selanjutnya dapat ditambahkan, jika diperlukan, untuk meneruskan argumen lebih lanjut ke konstruktor Dictionary maju dan mundur.

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
    ...
}

Contoh penggunaan, dengan kunci case-insensitive:

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
*/
Simon Tewsi
sumber
1

Ini kode saya. Semuanya adalah O (1) kecuali untuk konstruktor unggulan.

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();
    }
}
Iamsodarncool
sumber
Saya ingin tahu bagaimana konstruktor akan berperilaku jika Anda memberikan kamus yang ada di mana jenis kunci dan nilai sama. Bagaimana itu akan menyelesaikan apakah akan menggunakan yang mundur vs yang ke depan?
Colm Bhandal
0

Kelas encapsulating berikut menggunakan linq (IEnumerable Extensions) lebih dari 1 instance kamus.

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"];

    }

}
Brett Caswell
sumber
0

Ini menggunakan pengindeks untuk pencarian terbalik.
Pencarian terbalik adalah O (n) tetapi juga tidak menggunakan dua kamus

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;
        }
    } 
}
paparazzo
sumber
Misalnya: kemungkinan NRE dalam this[string Word]. Masalah tambahan adalah nama variabel yang tidak sesuai dengan praktik umum, komentar tidak konsisten dengan kode ( UInt16vs UInt32- itu sebabnya: jangan gunakan komentar!), Solusi tidak umum, ...
BartoszKP
0

Berikut solusi alternatif untuk yang disarankan. Menghapus kelas dalam dan memastikan koherensi saat menambahkan / menghapus item

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

sirpaddow
sumber
0

Ada sebuah BijectionDictionary tipe yang tersedia di repo open source ini:

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

Secara kualitatif tidak jauh berbeda dengan jawaban lain yang diberikan. Ini menggunakan dua kamus, seperti kebanyakan jawaban itu.

Apa yang baru, saya yakin, tentang kamus ini vs. jawaban lain sejauh ini, adalah bahwa alih-alih berperilaku seperti kamus dua arah, ia hanya berperilaku seperti kamus satu arah yang sudah dikenal dan kemudian secara dinamis memungkinkan Anda membalik kamus menggunakan properti Reverse. Referensi objek yang dibalik bersifat dangkal, sehingga masih dapat memodifikasi objek inti yang sama dengan referensi aslinya. Jadi Anda bisa memiliki dua referensi ke objek yang sama, kecuali salah satunya dibalik.

Hal lain yang mungkin unik tentang kamus ini adalah bahwa ada beberapa tes yang ditulis untuk itu dalam proyek pengujian di bawah repo itu. Ini telah digunakan oleh kami dalam praktik dan sejauh ini cukup stabil.

Colm Bhandal
sumber