일반 사전의 지정된 값의 여러 키를 가져 오십니까?
.NET 일반 사전에서 키 값을 쉽게 얻을 수 있습니다.
Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2]; // Beta
그러나 여러 키가있을 수 있으므로 값이 주어진 키를 얻는 것은 간단하지 않습니다.
int[] betaKeys = greek.WhatDoIPutHere("Beta"); // expecting single 2
다음은 다중 양방향 버전입니다.
using System;
using System.Collections.Generic;
using System.Text;
class BiDictionary<TFirst, TSecond>
{
IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();
private static IList<TFirst> EmptyFirstList = new TFirst[0];
private static IList<TSecond> EmptySecondList = new TSecond[0];
public void Add(TFirst first, TSecond second)
{
IList<TFirst> firsts;
IList<TSecond> seconds;
if (!firstToSecond.TryGetValue(first, out seconds))
{
seconds = new List<TSecond>();
firstToSecond[first] = seconds;
}
if (!secondToFirst.TryGetValue(second, out firsts))
{
firsts = new List<TFirst>();
secondToFirst[second] = firsts;
}
seconds.Add(second);
firsts.Add(first);
}
// Note potential ambiguity using indexers (e.g. mapping from int to int)
// Hence the methods as well...
public IList<TSecond> this[TFirst first]
{
get { return GetByFirst(first); }
}
public IList<TFirst> this[TSecond second]
{
get { return GetBySecond(second); }
}
public IList<TSecond> GetByFirst(TFirst first)
{
IList<TSecond> list;
if (!firstToSecond.TryGetValue(first, out list))
{
return EmptySecondList;
}
return new List<TSecond>(list); // Create a copy for sanity
}
public IList<TFirst> GetBySecond(TSecond second)
{
IList<TFirst> list;
if (!secondToFirst.TryGetValue(second, out list))
{
return EmptyFirstList;
}
return new List<TFirst>(list); // Create a copy for sanity
}
}
class Test
{
static void Main()
{
BiDictionary<int, string> greek = new BiDictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
greek.Add(5, "Beta");
ShowEntries(greek, "Alpha");
ShowEntries(greek, "Beta");
ShowEntries(greek, "Gamma");
}
static void ShowEntries(BiDictionary<int, string> dict, string key)
{
IList<int> values = dict[key];
StringBuilder builder = new StringBuilder();
foreach (int value in values)
{
if (builder.Length != 0)
{
builder.Append(", ");
}
builder.Append(value);
}
Console.WriteLine("{0}: [{1}]", key, builder);
}
}
다른 사람들이 말했듯이, 사전에는 값에서 키로의 매핑이 없습니다.
방금 값에서 여러 키로 매핑하려고한다는 것을 알았습니다.이 솔루션을 단일 값 버전으로 여기에두고 있지만 다중 항목 양방향 맵에 대한 다른 답변을 추가 할 것입니다.
여기서 취하는 일반적인 접근 방식은 두 가지 사전을 갖는 것입니다. 하나는 한 방법으로 매핑하고 다른 하나는 매핑합니다. 별도의 클래스로 캡슐화하고 키나 값이 중복 될 때 수행 할 작업 (예 : 예외 발생, 기존 항목 덮어 쓰기 또는 새 항목 무시)을 해결하십시오. 개인적으로 나는 예외를 던질 것입니다-성공 행동을 쉽게 정의 할 수있게합니다. 이 같은:
using System;
using System.Collections.Generic;
class BiDictionary<TFirst, TSecond>
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
if (firstToSecond.ContainsKey(first) ||
secondToFirst.ContainsKey(second))
{
throw new ArgumentException("Duplicate first or second");
}
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public bool TryGetByFirst(TFirst first, out TSecond second)
{
return firstToSecond.TryGetValue(first, out second);
}
public bool TryGetBySecond(TSecond second, out TFirst first)
{
return secondToFirst.TryGetValue(second, out first);
}
}
class Test
{
static void Main()
{
BiDictionary<int, string> greek = new BiDictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
int x;
greek.TryGetBySecond("Beta", out x);
Console.WriteLine(x);
}
}
키의 고유성은 보장되지만 값의 고유성은 보장되지 않기 때문에 사전은 실제로 이와 같이 작동하지 않습니다. 예를 들어
var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };
무엇을 기대 greek.WhatDoIPutHere("Alpha")
하십니까?
따라서 이와 같은 것을 프레임 워크에 넣을 것으로 기대할 수 없습니다. 고유 한 용도에 맞는 고유 한 방법이 필요합니다. 배열 (또는 IEnumerable<T>
) 을 반환 하시겠습니까? 주어진 값을 가진 여러 개의 키가 있다면 예외를 던지겠습니까? 없는 경우는 어떻습니까?
개인적으로 나는 다음과 같이 열거 할 수 있습니다.
IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
if (dict == null)
{
throw new ArgumentNullException("dict");
}
return dict.Keys.Where(k => dict[k] == val);
}
var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();
아마 Linq없이 가장 쉬운 방법은 쌍을 반복하는 것일 수 있습니다.
int betaKey;
foreach (KeyValuePair<int, string> pair in lookup)
{
if (pair.Value == value)
{
betaKey = pair.Key; // Found
break;
}
}
betaKey = -1; // Not found
Linq가 있다면 다음과 같이 쉽게 할 수 있습니다.
int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
사전은 값의 해시를 유지하지 않고 키만 유지하므로 값을 사용하여 검색하면 최소한 선형 시간이 걸립니다. 가장 좋은 방법은 단순히 사전의 요소를 반복하고 일치하는 키를 추적하거나 다른 데이터 구조로 전환하는 것입니다. 아마도 두 개의 사전 매핑 키-> 값 및 값-> List_of_keys를 유지하는 것이 좋습니다. 후자를 사용하면 검색 속도를 위해 스토리지를 교환합니다. @Cybis 예제를 그러한 데이터 구조로 바꾸는 데 많은 시간이 걸리지 않습니다.
본격적인 BiDirectional Dictionary (Map뿐만 아니라)를 원했기 때문에 누락 된 함수를 추가하여 그것을 IDictionary 호환 클래스로 만들었습니다. 고유 한 키-값 쌍이있는 버전을 기반으로합니다. 원하는 경우 파일은 다음과 같습니다 (대부분의 작업은 XMLDoc이었습니다).
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
/// <summary>Represents a bidirectional collection of keys and values.</summary>
/// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
/// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Diagnostics.DebuggerDisplay("Count = {Count}")]
//[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
//[System.Reflection.DefaultMember("Item")]
public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
{
IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
/// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
public IDictionary<TFirst, TSecond> KeyValue => this;
/// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;
#region Implemented members
/// <Summary>Gets or sets the value associated with the specified key.</Summary>
/// <param name="key">The key of the value to get or set.</param>
/// <Returns>The value associated with the specified key. If the specified key is not found,
/// a get operation throws a <see cref="KeyNotFoundException"/>, and
/// a set operation creates a new element with the specified key.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
/// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
/// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
/// <exception cref="T:System.ArgumentException"> An element with the same key already
/// exists in the <see cref="ValueKey"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public new TSecond this[TFirst key]
{
get { return base[key]; }
set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
}
/// <Summary>Gets or sets the key associated with the specified value.</Summary>
/// <param name="val">The value of the key to get or set.</param>
/// <Returns>The key associated with the specified value. If the specified value is not found,
/// a get operation throws a <see cref="KeyNotFoundException"/>, and
/// a set operation creates a new element with the specified value.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
/// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
/// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
/// <exception cref="T:System.ArgumentException"> An element with the same value already
/// exists in the <see cref="KeyValue"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public TFirst this[TSecond val]
{
get { return _ValueKey[val]; }
set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
}
/// <Summary>Adds the specified key and value to the dictionary.</Summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
/// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary<TFirst,TSecond>"/>.</exception>
public new void Add(TFirst key, TSecond value) {
base.Add(key, value);
_ValueKey.Add(value, key);
}
/// <Summary>Removes all keys and values from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
public new void Clear() { base.Clear(); _ValueKey.Clear(); }
/// <Summary>Determines whether the <see cref="Dictionary<TFirst,TSecond>"/> contains the specified
/// KeyValuePair.</Summary>
/// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary<TFirst,TSecond>"/>.</param>
/// <Returns>true if the <see cref="Dictionary<TFirst,TSecond>"/> contains an element with
/// the specified key which links to the specified value; otherwise, false.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);
/// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
/// <param name="item">The KeyValuePair to remove.</param>
/// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
/// method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);
/// <Summary>Removes the value with the specified key from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary>
/// <param name="key">The key of the element to remove.</param>
/// <Returns>true if the element is successfully found and removed; otherwise, false. This
/// method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);
/// <Summary>Gets the key associated with the specified value.</Summary>
/// <param name="value">The value of the key to get.</param>
/// <param name="key">When this method returns, contains the key associated with the specified value,
/// if the value is found; otherwise, the default value for the type of the key parameter.
/// This parameter is passed uninitialized.</param>
/// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value;
/// otherwise, false.</Returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
#endregion
}
}
수정 : 사전을 생각하면 일종의 열쇠이기 때문에 사전 이외의 것이 필요합니다. 즉, 값이 고유하지 않을 수 있습니다
그것은 당신이 c # 3.0을 사용하는 것처럼 보이기 때문에 루핑에 의존 할 필요가 없으며 다음과 같은 것을 사용할 수 있습니다 :
var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true) == 0 select k.Key).FirstOrDefault();
사전 클래스는이 경우에 최적화되어 있지 않지만 실제로 C # 2.0에서 수행하려면 다음을 수행하십시오.
public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
List<TKey> ks = new List<TKey>();
foreach(TKey k in dict.Keys)
{
if (dict[k] == val) { ks.Add(k); }
}
return ks;
}
우아함을 위해 LINQ 솔루션을 선호하지만 이것이 2.0 방식입니다.
해당 기능이있는 사전의 서브 클래스를 작성할 수 없습니까?
public class MyDict < TKey, TValue > : Dictionary < TKey, TValue >
{
private Dictionary < TValue, TKey > _keys;
public TValue this[TKey key]
{
get
{
return base[key];
}
set
{
base[key] = value;
_keys[value] = key;
}
}
public MyDict()
{
_keys = new Dictionary < TValue, TKey >();
}
public TKey GetKeyFromValue(TValue value)
{
return _keys[value];
}
}
편집 : 죄송합니다, 처음 코드를 얻지 못했습니다.
여기에 제안 된 "간단한"양방향 사전 솔루션은 복잡하며 이해, 유지 또는 확장하기 어려울 수 있습니다. 또한 원래 질문은 "값의 키"를 요구했지만 분명히 여러 키가있을 수 있습니다 (질문을 편집 한 이후). 전체적인 접근은 다소 의심 스럽다.
소프트웨어 변경. 유지하기 쉬운 코드 작성에는 다른 "영리한"복잡한 해결 방법이 우선적으로 적용되어야합니다. 사전의 값에서 키를 다시 얻는 방법은 반복하는 것입니다. 사전은 양방향으로 설계되지 않았습니다.
LINQ 를 사용 하여 역방향 Dictionary<K, V>
조회 를 수행 하십시오 . 그러나 값의 Dictionary<K, V>
값이 다르지 않을 수 있습니다.
데모:
using System;
using System.Collections.Generic;
using System.Linq;
class ReverseDictionaryLookupDemo
{
static void Main()
{
var dict = new Dictionary<int, string>();
dict.Add(4, "Four");
dict.Add(5, "Five");
dict.Add(1, "One");
dict.Add(11, "One"); // duplicate!
dict.Add(3, "Three");
dict.Add(2, "Two");
dict.Add(44, "Four"); // duplicate!
Console.WriteLine("\n== Enumerating Distinct Values ==");
foreach (string value in dict.Values.Distinct())
{
string valueString =
String.Join(", ", GetKeysFromValue(dict, value));
Console.WriteLine("{0} => [{1}]", value, valueString);
}
}
static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
{
// Use LINQ to do a reverse dictionary lookup.
// Returns a 'List<T>' to account for the possibility
// of duplicate values.
return
(from item in dict
where item.Value.Equals(value)
select item.Key).ToList();
}
}
예상 출력 :
== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";
foreach (string mk in dic.Keys)
{
if(dic[mk] == "Ahmed")
{
Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
}
}
키가 사전의 부호 값과 관련이 있다고 가정하면 허용되는 대답 ( https : //.com/a/255638/986160 ) 의 트위스트입니다 . ( https://stackoverflow.com/a/255630/986160 )과 비슷하지만 조금 더 우아합니다. 참신한 점은 소비 클래스가 열거 대안으로 사용될 수 있지만 (문자열도) 사전이 IEnumerable을 구현한다는 것입니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace MyApp.Dictionaries
{
class BiDictionary<TFirst, TSecond> : IEnumerable
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public TSecond this[TFirst first]
{
get { return GetByFirst(first); }
}
public TFirst this[TSecond second]
{
get { return GetBySecond(second); }
}
public TSecond GetByFirst(TFirst first)
{
return firstToSecond[first];
}
public TFirst GetBySecond(TSecond second)
{
return secondToFirst[second];
}
public IEnumerator GetEnumerator()
{
return GetFirstEnumerator();
}
public IEnumerator GetFirstEnumerator()
{
return firstToSecond.GetEnumerator();
}
public IEnumerator GetSecondEnumerator()
{
return secondToFirst.GetEnumerator();
}
}
}
그리고 소비 수업으로
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyApp.Dictionaries
{
class Greek
{
public static readonly string Alpha = "Alpha";
public static readonly string Beta = "Beta";
public static readonly string Gamma = "Gamma";
public static readonly string Delta = "Delta";
private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();
static Greek() {
Dictionary.Add(1, Alpha);
Dictionary.Add(2, Beta);
Dictionary.Add(3, Gamma);
Dictionary.Add(4, Delta);
}
public static string getById(int id){
return Dictionary.GetByFirst(id);
}
public static int getByValue(string value)
{
return Dictionary.GetBySecond(value);
}
}
}
그런 다음 평신도의 해결책
이러한 사전을 만들기 위해 아래 기능과 유사한 기능을 작성할 수 있습니다.
public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
'Programing' 카테고리의 다른 글
여러 EditText에 단일 TextWatcher를 사용하는 방법은 무엇입니까? (0) | 2020.07.18 |
---|---|
mailto는 여러 본문을 연결합니다 (0) | 2020.07.18 |
Catch없이 Java Try Catch 최종적으로 차단 (0) | 2020.07.18 |
JavaScript는 이름으로 요소를 가져옵니다. (0) | 2020.07.18 |
ConnectionTimeout과 SocketTimeout (0) | 2020.07.18 |