Từ điển hai chiều / hai chiều trong C #?


86

Tôi muốn lưu trữ các từ trong từ điển theo cách sau:

Tôi có thể nhận mã từng từ: dict["SomeWord"]-> 123và nhận từng mã từng từ: dict[123]->"SomeWord"

Nó có thật không? Tất nhiên một cách để làm điều đó là hai từ điển: Dictionary<string,int>Dictionary<int,string>còn cách nào khác không?


2
Không có kiểu dữ liệu chuẩn (kể từ .NET 4) cung cấp quyền truy cập O (1) theo cả hai cách ... AFAIK :)

Cũng không phải là bản đồ hai chiều (từ khóa?) Áp đặt các hạn chế bổ sung, trừ khi bản đồ đa hướng ...

Câu trả lời:


108

Tôi đã viết một vài lớp học nhanh chóng cho phép bạn làm những gì bạn muốn. Bạn có thể cần phải mở rộng nó với nhiều tính năng hơn, nhưng đó là một điểm khởi đầu tốt.

Việc sử dụng mã trông như thế này:

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

map.Add(42, "Hello");

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

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

Đây là định nghĩa:

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 - Hiện tại. ;-)
Enigmativity

2
@ Pedro77 - Tôi đã rất táo bạo khi đề xuất rằng lớp của tôi là giải pháp "bản đồ" mới.
Bí ẩn

11
Điều này không duy trì các bất biến của lớp trên các ngoại lệ. Có thể _forward.Addthành công và _reverse.Addthất bại, để lại cho bạn một cặp được bổ sung một phần.

5
@hvd - Như tôi đã nói - đó là một lớp tập hợp nhanh chóng.
Enigmativity

3
@AaA Nó không sửa đổi thuộc tính Forwardtừ điển mà nó tự có (mà nó có private set;), nhưng nó đang sửa đổi giá trị trên từ điển đó thông qua thuộc tính Indexer của lớp Indexer chuyển nó vào từ điển. public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } }Vì vậy, đó là phá vỡ tra cứu thuận / ngược.
Jeroen van Langen

26

Thật không may, bạn cần hai từ điển, một cho mỗi hướng. Tuy nhiên, bạn có thể dễ dàng lấy từ điển nghịch đảo bằng LINQ:

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

10

Đã mở rộng trên mã Enigmativity bằng cách thêm phương thức khởi tạo và phương thức Chứa.

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

Đây là một trường hợp sử dụng, hãy kiểm tra các dấu ngoặc hợp lệ

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

Bạn có thể sử dụng hai từ điển, như những người khác đã nói, nhưng cũng lưu ý rằng nếu cả hai TKeyTValuecùng loại (và miền giá trị thời gian chạy của chúng được biết là rời rạc) thì bạn chỉ có thể sử dụng cùng một từ điển bằng cách tạo hai mục nhập cho mỗi khóa / value ghép nối:

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

Bằng cách này, một từ điển duy nhất có thể được sử dụng cho một trong hai loại tra cứu.


3
Vâng, phương pháp này đã được thừa nhận trong câu hỏi :)

3
Điều này bỏ qua khả năng tồn tại cùng một giá trị trong cả "khóa" và "giá trị". thì nó sẽ xung đột trong giải pháp này.
user1028741

1
@ user1028741 Đồng ý, mặc dù từ ví dụ nó xuất hiện rằng họ có nghĩa là "một loại khác nhau" không "cùng loại"
Hutch

6

Cái quái gì vậy, tôi sẽ ném phiên bản của mình vào hỗn hợp:

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

Thêm một số dữ liệu vào nó:

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

Và sau đó thực hiện tra cứu:

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

Tôi thích điều đó hỗ trợ này 1: N
Sebastian

@Sebastian, bạn có thể thêm IEnumerable <KeyValuePair <TKey, TValue >>.
Ostati

4

Bạn có thể sử dụng phương pháp mở rộng này, mặc dù nó sử dụng kiểu liệt kê và do đó có thể không hiệu quả đối với các tập dữ liệu lớn. Nếu bạn lo lắng về hiệu quả, thì bạn cần hai từ điển. Nếu bạn muốn gộp hai từ điển vào một lớp, hãy xem câu trả lời được chấp nhận cho câu hỏi này: Từ điển 2 chiều 1 đến 1 trong 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");
    }
}

8
Mặc dù đây là một từ điển hai chiều, việc tìm nạp Giá trị là một hoạt động O (n) trong đó nó phải là một hoạt động O (1). Điều này có thể không thành vấn đề đối với các tập dữ liệu nhỏ, nhưng có thể gây ra các vấn đề về hiệu suất khi làm việc với các tập dữ liệu lớn. Câu trả lời tốt nhất cho hiệu suất trên không gian là sử dụng hai từ điển với dữ liệu được đảo ngược.
Tom

@TomA Tôi hoàn toàn đồng ý với Tom, trường hợp duy nhất mà bạn cần một từ điển hai chiều thực sự là khi bạn có 100K, 1M + mục nhập, bất cứ thứ gì ít quét hơn thì nó thực sự là một NOOP.
Chris Marisic

Tôi thích giải pháp này cho tình huống của tôi (kích thước dict nhỏ) vì tôi vẫn có thể sử dụng bộ khởi tạo bộ sưu tập. Ánh xạ <A, B> trong câu trả lời được chấp nhận, tôi không nghĩ có thể được sử dụng trong bộ khởi tạo bộ sưu tập.
CVertex

@ChrisMarisic, đó có vẻ là một điều kỳ lạ khi tuyên bố. Nếu việc tra cứu này được gọi trong một vòng lặp chặt chẽ, tôi cá, bạn sẽ cảm thấy đau đớn ngay cả với <500 mục nhập. Nó cũng phụ thuộc vào chi phí của một thử nghiệm so sánh. Tôi không nghĩ rằng những tuyên bố sâu rộng như bình luận của bạn là hữu ích.
Lee Campbell

@LeeCampbell Các tuyên bố sâu rộng của tôi dựa trên kinh nghiệm trong thực tế thực tế, cũng như trong thực tế có thể đo lường và mô tả. Nếu bạn muốn sử dụng một số kiểu phức tạp làm khóa cho từ điển, đó là vấn đề của bạn không phải vấn đề của tôi.
Chris Marisic

1

Xe đạp

Đây là tổng hợp những gì tôi thích trong mỗi câu trả lời. Nó thực hiện IEnumerableđể nó có thể sử dụng bộ khởi tạo bộ sưu tập, như bạn có thể thấy trong ví dụ.

Hạn chế sử dụng:

  • Bạn đang sử dụng các kiểu dữ liệu khác nhau. (tức là, )T1T2

Mã:

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

Vĩ cầm:

https://dotnetfiddle.net/mTNEuw


Giải pháp rất thanh lịch! Bạn có thể giải thích sâu hơn về nó một chút không? Tôi có đúng không, rằng bạn không thể tạo Bictionary<string, string>ngay cả khi tất cả các chuỗi là duy nhất?
Marcus Mangelsdorf

@ Merlin2001, Đúng vậy. Chính xác hơn, bạn không thể thực hiện tra cứu với điều đó. Tôi sẽ phải nghĩ về cách vượt qua điều đó. Nó biên dịch nhưng nó luôn tìm thấy trình lập chỉ mục ngược đầu tiên khi T1 == T2, vì vậy việc tra cứu về phía trước không thành công. Hơn nữa, tôi không thể ghi đè trình lập chỉ mục mặc định vì sau đó các cuộc gọi tra cứu sẽ không rõ ràng. Tôi đã thêm ràng buộc này và xóa ràng buộc trước đó, vì các giá trị của T1có thể trùng lặp với các giá trị của T2.
toddmo

10
Ngược lại, có một vấn đề hiệu suất khá nghiêm trọng; từ điển được tìm kiếm hai lần, với một lần nhấn hiệu suất O (n); sẽ nhanh hơn nhiều nếu sử dụng từ điển thứ hai và sẽ loại bỏ ràng buộc loại.
Steve Cooper

@SteveCooper, có lẽ tôi có thể loại bỏ hiệu suất bị ảnh hưởng bằng cách gói nó trong một tryvà chuyển đổi ngoại lệ thành KeyNotFoundExceptions.
toddmo

4
@toddmo, bạn có thể tăng gấp đôi tốc độ theo cách này. Vấn đề lớn hơn là cả .First và .Any đều tìm kiếm từng mục một, kiểm tra từng mục. Vì vậy, để kiểm tra danh sách 1.000.000 mục cần tìm kiếm lâu hơn 1.000.000 lần so với danh sách 1 phần tử. Từ điển nhanh hơn nhiều và không bị chậm lại khi bạn thêm nhiều mục hơn, vì vậy từ điển nghịch đảo thứ hai sẽ tiết kiệm lượng thời gian lớn hơn so với danh sách lớn. Nó có thể không liên quan, nhưng nó là một thứ có thể ổn với một lượng nhỏ dữ liệu trong quá trình thử nghiệm, và sau đó nó giết chết hiệu suất trên một máy chủ thực với dữ liệu nghiêm trọng.
Steve Cooper

1

Đây là một vấn đề cũ nhưng tôi muốn thêm hai phương thức mở rộng trong trường hợp bất kỳ ai thấy nó hữu ích. Thứ hai không hữu ích bằng nhưng nó cung cấp một điểm khởi đầu nếu một từ điển cần được hỗ trợ.

    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

Một phiên bản sửa đổi của câu trả lời của Xavier John, với một hàm tạo bổ sung để sử dụng Trình so sánh thuận và ngược. Ví dụ, điều này sẽ hỗ trợ các khóa không phân biệt chữ hoa chữ thường. Các hàm tạo khác có thể được thêm vào, nếu cần, để chuyển các đối số khác tới các hàm tạo Từ điển thuận và ngược.

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

Ví dụ về cách sử dụng, với khóa không phân biệt chữ hoa chữ thường:

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

Đây là mã của tôi. Mọi thứ đều là O (1) ngoại trừ các hàm tạo được gieo mầm.

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

Tôi tự hỏi phương thức khởi tạo sẽ hoạt động như thế nào nếu bạn chuyển nó vào một từ điển hiện có, nơi các loại khóa và giá trị giống nhau. Làm thế nào nó sẽ giải quyết nếu sử dụng ngược lại so với chuyển tiếp?
Colm Bhandal

0

Lớp đóng gói sau sử dụng linq (IEnumerable Extensions) trên 1 phiên bản từ điển.

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

Điều này sử dụng một chỉ mục để tra cứu ngược lại.
Tra cứu ngược lại là O (n) nhưng nó cũng không sử dụng hai từ điển

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

Ví dụ: NRE có thể trong this[string Word]. Những vấn đề khác là tên biến không tương ứng với thông lệ chung, bình luận không phù hợp với mã ( UInt16vs UInt32- đó là lý do: không sử dụng ý kiến), giải pháp không phải là chung chung, ...
BartoszKP

0

Đây là một giải pháp thay thế cho những giải pháp đã được đề xuất. Loại bỏ lớp bên trong và đảm bảo tính nhất quán khi thêm / bớt các mục

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

Đây là một BijectionDictionary loại có sẵn trong repo mã nguồn mở này:

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

Nó không khác nhiều về chất so với các câu trả lời khác được đưa ra. Nó sử dụng hai từ điển, giống như hầu hết các câu trả lời đó.

Tôi tin rằng tiểu thuyết là gì, về từ điển này so với các câu trả lời khác cho đến nay, là thay vì hoạt động như từ điển hai chiều, nó chỉ hoạt động như từ điển một chiều, quen thuộc và sau đó tự động cho phép bạn lật từ điển bằng cách sử dụng thuộc tính Reverse. Tham chiếu đối tượng đã lật là nông, vì vậy nó vẫn có thể sửa đổi đối tượng cốt lõi giống như tham chiếu ban đầu. Vì vậy, bạn có thể có hai tham chiếu đến cùng một đối tượng, ngoại trừ một trong số chúng bị lật.

Một điều khác có lẽ là duy nhất về từ điển này là có một số bài kiểm tra được viết cho nó trong dự án thử nghiệm trong repo đó. Nó đã được chúng tôi sử dụng trong thực tế và hoạt động khá ổn định cho đến nay.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.