2014-12-30 73 views
3

我有兩個字典一樣Dic1<string,string>Dic2<string,string> 我想產生的值的同時存在於兩個Dic1Dic2 因此,例如密鑰的新list的交集如果找到兩個詞典

Dic1: <12,"hi">, <14,"bye"> 
Dic2: <12,"hello">, <18,"bye"> 

然後列表應該是:"hi" , "hello"

我試圖與Dic1.Keys.Intersect一起工作,但還不能完全弄清楚。

What I tried: Dic1.Keys.Intersect(Dic2.Keys).ToList(t => t.Values); 
+0

您可以創建一個交叉'​​字典首先通過該字典中的所有鍵來獲取列表。看到這篇文章http://stackoverflow.com/questions/10685142/c-sharp-dictionaries-intersect – locoboy

回答

3

給你:

var dic1 = new Dictionary<int, string> { { 12, "hi" }, { 14, "bye" } }; 
var dic2 = new Dictionary<int, string> { { 12, "hello" }, { 18, "bye" } }; 
HashSet<int> commonKeys = new HashSet<int>(dic1.Keys); 
commonKeys.IntersectWith(dic2.Keys); 
var result = 
    dic1 
    .Where(x => commonKeys.Contains(x.Key)) 
    .Concat(dic2.Where(x => commonKeys.Contains(x.Key))) 
    // .Select(x => x.Value) // With this additional select you'll get only the values. 
    .ToList(); 

結果列表包含{ 12, "hi" }{ 12, "hello" }

HashSet是交叉非常有用的。


剛走出curiostiy的我相比,所有六個解決方案(希望沒有錯過任何)和時間如下:

@EZI  Intersect2 GroupBy   ~149ms 
@Selman22 Intersect3 Keys.Intersect ~41ms 
@dbc  Intersect4 Where1   ~22ms 
@dbc  Intersect5 Where2   ~18ms 
@dbc  Intersect5 Classic   ~11ms 
@t3chb0t Intersect1 HashSet   ~66ms 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var dic1 = new Dictionary<int, string>(); 
     var dic2 = new Dictionary<int, string>(); 

     Random rnd = new Random(DateTime.Now.Millisecond); 
     for (int i = 0; i < 100000; i++) 
     { 
      int id = 0; 

      do { id = rnd.Next(0, 1000000); } while (dic1.ContainsKey(id)); 
      dic1.Add(id, "hi"); 

      do { id = rnd.Next(0, 1000000); } while (dic2.ContainsKey(id)); 
      dic2.Add(id, "hello"); 
     } 

     List<List<string>> results = new List<List<string>>(); 

     using (new AutoStopwatch(true)) { results.Add(Intersect1(dic1, dic2)); } 
     Console.WriteLine("Intersect1 elapsed in {0}ms (HashSet)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     using (new AutoStopwatch(true)) { results.Add(Intersect2(dic1, dic2)); } 
     Console.WriteLine("Intersect2 elapsed in {0}ms (GroupBy)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     using (new AutoStopwatch(true)) { results.Add(Intersect3(dic1, dic2)); } 
     Console.WriteLine("Intersect3 elapsed in {0}ms (Keys.Intersect)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     using (new AutoStopwatch(true)) { results.Add(Intersect4(dic1, dic2)); } 
     Console.WriteLine("Intersect4 elapsed in {0}ms (Where1)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     using (new AutoStopwatch(true)) { results.Add(Intersect5(dic1, dic2)); } 
     Console.WriteLine("Intersect5 elapsed in {0}ms (Where2)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     using (new AutoStopwatch(true)) { results.Add(Intersect7(dic1, dic2)); } 
     Console.WriteLine("Intersect7 elapsed in {0}ms (Old style :-)", AutoStopwatch.Stopwatch.ElapsedMilliseconds); 

     Console.ReadKey(); 
    } 

    static List<string> Intersect1(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     HashSet<int> commonKeys = new HashSet<int>(dic1.Keys); 
     commonKeys.IntersectWith(dic2.Keys); 
     var result = 
      dic1 
      .Where(x => commonKeys.Contains(x.Key)) 
      .Concat(dic2.Where(x => commonKeys.Contains(x.Key))) 
      .Select(x => x.Value) 
      .ToList(); 
     return result; 
    } 

    static List<string> Intersect2(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var result = dic1.Concat(dic2) 
        .GroupBy(x => x.Key) 
        .Where(g => g.Count() > 1) 
        .SelectMany(g => g.Select(x => x.Value)) 
        .ToList(); 
     return result; 
    } 

    static List<string> Intersect3(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var result = 
      dic1 
      .Keys 
      .Intersect(dic2.Keys) 
      .SelectMany(key => new[] { dic1[key], dic2[key] }) 
      .ToList(); 
     return result; 
    } 

    static List<string> Intersect4(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var result = 
      dic1. 
      Where(pair => dic2.ContainsKey(pair.Key)) 
      .SelectMany(pair => new[] { dic2[pair.Key], pair.Value }).ToList(); 
     return result; 
    } 

    static List<string> Intersect5(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var result = 
      dic1 
      .Keys 
      .Where(dic2.ContainsKey).SelectMany(k => new[] { dic1[k], dic2[k] }) 
      .ToList(); 
     return result; 
    } 

    static List<string> Intersect7(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var list = new List<string>(); 
     foreach (var key in dic1.Keys) 
     { 
      if (dic2.ContainsKey(key)) 
      { 
       list.Add(dic1[key]); 
       list.Add(dic2[key]); 
      } 
     } 
     return list; 
    } 
} 

class AutoStopwatch : IDisposable 
{ 
    public static readonly Stopwatch Stopwatch = new Stopwatch(); 

    public AutoStopwatch(bool start) 
    { 
     Stopwatch.Reset(); 
     if (start) Stopwatch.Start(); 
    } 
    public void Dispose() 
    { 
     Stopwatch.Stop(); 
    } 
} 
+1

_那麼列表應該是:「嗨」,「你好」_而且是。將字符串抽出不是問題,您甚至可以獲得更多信息供以後使用。我添加了一個'選擇':-) – t3chb0t

+2

「通過這個額外的選擇,你只會得到這些值。」價值觀就是他們所要求的大聲笑。這就是爲什麼我提起它。 –

+0

我猜我錯過了它,因爲我讀了字典交集,並寫了代碼,沒有想到對結果的懷疑。我所知道的是,鑰匙應該是常見的: - ] – t3chb0t

0

你只需要使用索引來獲取值:

var values = Dic1.Keys.Intersect(Dic2.Keys) 
      .SelectMany(key => new[] { Dic1[key], Dic2[key] }) 
      .ToList(); 
+1

@downvoters(EZI除外),如果這個答案有什麼問題讓我知道,或者如果你只是幼稚讓我知道,要麼不要低估和隱藏自己,讓我們看看你的理由,如果有的話。 –

2

可以使用Where濾波器DIC1鑰匙,然後將其轉換爲值,像這樣:

var values = Dic1.Keys.Where(Dic2.ContainsKey).SelectMany(k => new[] { Dic1[k], Dic2[k] }) 
    .ToList(); 

這應該與Dic1Dic2上的查找操作一樣高效,該操作通常是log(n)或更好,並且不需要構建任何臨時哈希集或查找表。

下面是避免了被少一點相當的成本字典查找的一個版本:

var values = Dic1.Where(pair => Dic2.ContainsKey(pair.Key)).SelectMany(pair => new[] { pair.Value, Dic2[pair.Key] }) 
    .ToList(); 

更新

我的時間測試(使用t3chb0t的方便測試工具)顯示第一個版本實際運行得更快這很簡單,所以在這兩個,更喜歡這一點。但到目前爲止,我已經找到了最快不使用Linq可言,在7毫秒達到1000000路交叉口對13對LINQ的版本:

static List<string> Intersect7(Dictionary<int, string> dic1, Dictionary<int, string> dic2) 
    { 
     var list = new List<string>(); 
     foreach (var key in dic1.Keys) 
     { 
      if (dic2.ContainsKey(key)) 
      { 
       list.Add(dic1[key]); 
       list.Add(dic2[key]); 
      } 
     } 
     return list; 
    } 

這是一個古老的風格,但這樣你可能不希望這樣。

+0

這隻會從Dic1抓住「hi」 –

+0

@austinwernli - 修好了,謝謝! – dbc

+1

Downvoter - 請問這是什麼問題? – dbc

5
var d1 = new Dictionary<int, string>() { { 12, "hi" }, { 14, "bye" } }; 
var d2 = new Dictionary<int, string>() { { 12, "hello" }, { 18, "bye" } }; 

var res = d1.Concat(d2) 
      .GroupBy(x => x.Key) 
      .Where(g => g.Count() > 1) 
      .SelectMany(g => g.Select(x=>x.Value)) 
      .ToList(); 
+0

你能解釋一下g.Count()> 1是什麼?謝謝。 – Bohn

+1

@Blake它意味着它是常見的兩個字。如果它是1,那麼它意味着它只有一個字典。 – EZI

+0

既不需要Concat也不需要GroupBy。問題中的相交可以正常工作。這只是開銷。 –