[c#] C #의 양방향 / 양방향 사전?

다음과 같은 방법으로 사전에 단어를 저장하고 싶습니다.

단어 별 코드를 얻을 수 있습니다 : dict["SomeWord"]-> 123그리고 단어 별 코드를 얻을 수 있습니다 : dict[123]->"SomeWord"

진짜야? 물론 하나의 방법은 두 가지 사전입니다해야 할 일 : Dictionary<string,int>Dictionary<int,string>하지만 또 다른 방법은 무엇입니까?



답변

나는 당신이 원하는 것을 할 수 있도록 몇 가지 간단한 수업을 썼습니다. 더 많은 기능으로 확장해야 할 수도 있지만 좋은 시작점입니다.

코드 사용은 다음과 같습니다.

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

map.Add(42, "Hello");

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

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

정의는 다음과 같습니다.

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


답변

안타깝게도 각 방향에 하나씩 두 개의 사전이 필요합니다. 그러나 LINQ를 사용하여 쉽게 역 사전을 가져올 수 있습니다.

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


답변

initializes 및 Contains 메서드를 추가하여 Enigmativity 코드에서 확장되었습니다.

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

다음은 유스 케이스입니다. 유효한 괄호를 확인하십시오.

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


답변

다른 사람들이 말했듯이 두 개의 사전을 사용할 수 있지만 TKey및 둘 다 TValue동일한 유형 (및 해당 런타임 값 도메인이 분리 된 것으로 알려진 경우)이면 각 키에 대해 두 개의 항목을 생성하여 동일한 사전을 사용할 수 있습니다. / 값 쌍 :

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

이렇게하면 두 유형의 조회에 단일 사전을 사용할 수 있습니다.


답변

도대체 내 버전을 믹스에 넣겠습니다.

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

여기에 데이터를 추가하십시오.

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

그런 다음 조회를 수행하십시오.

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


답변

이 확장 메서드는 열거 형을 사용하지만 사용할 수 있으므로 큰 데이터 집합의 경우 성능이 떨어질 수 있습니다. 효율성이 걱정된다면 두 개의 사전이 필요합니다. 두 사전을 하나의 클래스로 래핑하려면이 질문에 대해 허용되는 답변을 참조하십시오. 양방향 일대일 사전 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");
    }
}


답변

Bictionary

다음은 각 답변에서 내가 좋아하는 것을 혼합 한 것입니다. IEnumerable예제에서 볼 수 있듯이 컬렉션 이니셜 라이저를 사용할 수 있도록 구현 합니다.

사용 제한 :

  • 다른 데이터 유형을 사용하고 있습니다. (예 )T1T2

암호:

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

깡깡이:

https://dotnetfiddle.net/mTNEuw