我想存儲在字典中的單詞方式如下:C#中的雙向/雙向字典?
我可以搭話三字代碼:dict["SomeWord"]
- >123
和字碼得到一句話:dict[123]
- >"SomeWord"
是真的嗎?當然,有一種方法可以做到兩個字典:Dictionary<string,int>
和Dictionary<int,string>
,但有沒有另外一種方法?
我想存儲在字典中的單詞方式如下: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; }
}
不錯,.NET仍然沒有他們的「地圖」解決方案? – Pedro77
@ Pedro77 - 它現在。 ;-) – Enigmativity
所以,你不喜歡我的編輯。你能分享一下我們這個新的.NET類嗎? – Pedro77
你可以使用兩個字典,如你所說,或者如果這兩個鍵和值是相同類型的,你可以只使用一個:
dict["SomeWord"]= "123"
和dict["123"]="SomeWord"
所有使用它的查找。
是的,這個方法在問題中得到了肯定:) – 2012-06-10 04:32:13
這忽略了「keys」和「values」中存在相同值的可能性。那麼這個解決方案會衝突。 – user1028741
@ user1028741同意,雖然從例如它似乎是指「不同類型的」非「相同類型的」 – Hutch
雖然它使用枚舉,但您可以使用此擴展方法,因此可能不適用於大型數據集。如果你擔心效率,那麼你需要兩本字典。如果你想兩個庫包裝成一個類,請參閱接受的答案對這個問題:Bidirectional 1 to 1 Dictionary in 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");
}
}
雖然這是一個雙向的字典,取該值是一個爲O(n)操作,其中它應該是一個O(1)操作。這對於小數據集可能無關緊要,但在處理大數據集時可能會導致性能問題。對空間性能的最佳答案是使用反向數據的兩個字典。 – Tom
@TomA我完全贊同湯姆,你需要一個真正的雙向字典的唯一實例是當你有100K,1M +的條目,更少的掃描它實際上是一個NOOP。 –
我喜歡這種解決方案適合我的情況(小字典大小),因爲我仍然可以使用集合初始值設定項。地圖在接受的答案我不認爲可以用於收集初始值設定項。 – CVertex
它使用反向查找索引器。
反向查找是O(n),但它也不會超過1項字典實例使用兩個字典
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;
}
}
}
例如:'this [string Word]'中可能的NRE。其他問題是變量名稱不符合常規做法,評論與代碼不一致('UInt16' vs'UInt32' - 這就是爲什麼:不使用註釋!),解決方案不是通用的,... – BartoszKP
以下包封類利用LINQ(IEnumerable的擴展)。
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"];
}
}
下面是我在回答每一個喜歡攙和。它實現IEnumerable
,因此它可以使用集合初始值設定項,如您在示例中所見。
使用限制:
T1
≠
T2
)代碼:
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;
}
}
}
小提琴:
非常優雅的解決方案!你能解釋一下它的膽量嗎?我是對的,即使所有的字符串都是唯一的,你也不能創建一個'Bictionary
@ Merlin2001,這是正確的。更確切地說,你不能用它進行前向查找。我將不得不考慮如何克服這一點。它會編譯,但是當'T1 == T2'時它總是首先找到反向索引器,所以前向查找失敗。此外,我無法重寫默認索引器,因爲查找調用會不明確。我添加了這個約束並刪除了前一個,因爲'T1'的值可以與'T2'的值重疊。 – toddmo
背面存在相當嚴重的性能問題;字典被搜索兩次,O(n)表演命中;使用第二個字典會更快,並且會刪除類型約束。 –
通過添加初始化和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);
}
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;
}
}
這是一個老問題,但我想補充兩個擴展方法的情況下,任何人發現它是有用的。第二個不是很有用,但它提供了一個起點,如果需要支持一對一的字典。
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;
}
沒有標準(如.NET 4)的數據類型,其提供O(1)訪問兩種方式... AFAIK :) – 2012-06-10 04:29:47
而且不是雙向映射(關鍵字?)強加額外限制,除非多雙向地圖... – 2012-06-10 04:37:20
http://stackoverflow.com/questions/1227683/bi-directional-dictionary,http://stackoverflow.com/questions/268321/bidirectional-1-to-1- dictionary-in-c-sharp – 2012-06-10 04:37:49