2012-04-11 45 views
5

我有两个排序的字典两者同类型签名的压缩和解/合并两个排序列表

SortedDictionary<decimal, long> A 
SortedDictionary<decimal, long> B 

我要合并两个列表,其中的关键是一样的,这样就产生一个新的列表像

SortedDictionary<decimal, KeyValuePair<long,long>> 
or 
SortedDictionary<decimal, List<long>> 

这可能不是approacing情况的最好办法,但可能有人给我抬起头,就如何做到这一点还是一个更好的方式来处理它。

+0

为什么第二个是KeyValuePair?不应该只是一个列表? – Tigran 2012-04-11 12:35:45

+0

列表将工作。将其添加到 – Abstract 2012-04-11 12:37:37

+3

试图确保我理解您的内容 - 是否希望初始字典中只有*一个*的元素出现在输出字典中,或者只有*键出现在两个字典中? @Tigran--希望这会清除一切 – 2012-04-11 13:11:30

回答

5

这是我的本钱:

SortedDictionary<decimal, List<long>> merged = new SortedDictionary<decimal, List<long>> 
(
    A.Union(B) 
    .ToLookup(x => x.Key, x => x.Value) 
    .ToDictionary(x => x.Key, x => new List<long>(x)) 
); 

编辑:上述解决方案选择不包含在这两个集合中的关键字。这应该选择其中键是一样的:

SortedDictionary<decimal, List<long>> merged = new SortedDictionary<decimal, List<long>> 
(
    A.Where(x=>B.ContainsKey(x.Key)) 
    .ToDictionary(x => x.Key, x => new List<long>(){x.Value, B[x.Key]}) 
); 
+0

oooo我认为这是一个非常干净的方式接近它!我喜欢.ToLookup()调用比GroupBy更好 - 使我更容易理解 – 2012-04-11 13:17:43

+0

OP应该不会进一步回答:) – NSGaga 2012-04-11 13:23:51

+0

“我想合并两个列表,其中键是相同的。”您的答案包括不在两个词典中的键。 – jason 2012-04-11 13:26:52

0

这样做的另一个LINQ的方式,我认为抓住了一套操作方面的意图更好:

SortedDictionary<decimal, long> a = new SortedDictionary<decimal, long>(); 
SortedDictionary<decimal, long> b = new SortedDictionary<decimal, long>(); 

a.Add(0, 10); 
a.Add(1, 10); 
a.Add(2, 100); 
a.Add(100, 1); 

b.Add(0, 4); 
b.Add(4, 4); 
b.Add(2, 10); 

var result = a.Union(b) 
    .GroupBy(x => x.Key) 
    .ToDictionary(x => x.Key, x => x.Select(y => (long)y.Value).ToList()); 
+0

这不会添加两个字典中没有**相同**键的元素。 – Tigran 2012-04-11 13:04:49

+0

请注意,我不知道是否需要输出为“SortedDictionary”。如果是这样,这不难解决。 – 2012-04-11 13:05:04

+0

@Tigran - OP声明'我想合并两个关键字相同的列表'。我认为这意味着一个联盟 – 2012-04-11 13:07:28

0

尝试是这样的,它并不容易:

Dictionary<decimal, long> dic1 = new Dictionary<decimal, long>{ {3,23}, {2,3}, {5,4}, {6,8}}; 
    Dictionary<decimal, long> dic2 = new Dictionary<decimal, long>{ {3,2}, {2,5}, {5,14}, {12,2}}; 


    //recover shared keys (the keys that are present in both dictionaries) 
    var sharedKeys = dic1.Select(dic => dic.Key).Intersect(dic2.Select(d2=>d2.Key)); 
    sharedKeys.Dump(); 

    //add to the fìnal dictionary 
    var final = new Dictionary<decimal, List<long>>(); 
    foreach(var shk in sharedKeys) { 

     if(!final.ContainsKey(shk)) 
      final[shk] = new List<long>(); 

     final[shk].Add(dic1[shk]); 
     final[shk].Add(dic2[shk]); 
    } 

**EDIT** 
//Skip below part if you need only keys present on both dictionaries. 
///----------------------------------------------------------------- 

    //get unique keys present in Dic1 and add 
    var nonsharedkeys1 = dic1.Select(d=>d.Key).Where(k=>!sharedKeys.Contains(k)); 
    foreach(var nshk in nonsharedkeys1) { 

     final[nshk] = new List<long>();    
     final[nshk].Add(dic1[nshk]);   
    } 

    //get unique keys present in Dic2 and add 
    var nonsharedkeys2 = dic2.Select(d=>d.Key).Where(k=>!sharedKeys.Contains(k)); 
    foreach(var nshk in nonsharedkeys2) { 

     final[nshk] = new List<long>();    
     final[nshk].Add(dic2[nshk]);   
    } 

应该工作为你。

0

你可以简单地使用LINQ做到这一点:

var query = from a in A 
      join b in B 
       on a.Key equals b.Key 
      select new { 
       Key = a.Key, 
       Value = Tuple.Create(a.Value, b.Value) 
      }; 
var merged = new SortedDictionary<decimal, Tuple<long, long>>(
       query.ToDictionary(x => x.Key, x => x.Value) 
      ); 

我想你应该在合并后的字典使用Tuple<long, long>为您TValue

+0

你的结果与我用相同的数据运行时得到的扩展语法不同。你们产生:{{0,{10,4},{2,{100,10}}}。矿产生:{{0,{10,4}},{1,{10}},{2,{100,10}},{100,{1}},{4,{4}}}我的文章中的示例条目) – 2012-04-11 13:16:57

+0

不适用于'list1:{{1,1} {2,2}}和'list2:{{2,2} {3,3}} – 2012-04-11 13:17:09

+0

@LB:我们清楚地理解不同的要求。在你的例子中,我的产生'{2,(2,2)}'。这似乎符合“我想合并两个关键字相同的列表”的要求。请告诉我你是如何理解它的。我们中只有一个可以是对的。 – jason 2012-04-11 13:20:08

0

你可以“滥用” ConcatAggregate这样的:

var A = new SortedDictionary<decimal,long>(); 
var B = new SortedDictionary<decimal,long>(); 

A.Add(1, 11); 
A.Add(2, 22); 
A.Add(3, 33); 

B.Add(2, 222); 
B.Add(3, 333); 
B.Add(4, 444); 

var C = A.Concat(B).Aggregate(
    new SortedDictionary<decimal, List<long>>(), 
    (result, pair) => { 
     List<long> val; 
     if (result.TryGetValue(pair.Key, out val)) 
      val.Add(pair.Value); 
     else 
      result.Add(pair.Key, new[] { pair.Value }.ToList()); 
     return result; 
    } 
); 

foreach (var x in C) 
    Console.WriteLine(
     string.Format(
      "{0}:\t{1}", 
      x.Key, 
      string.Join(", ", x.Value) 
     ) 
    ); 

输出结果:

1:  11 
2:  22, 222 
3:  33, 333 
4:  444 

这几乎是一样的,如果你写了一个“正常”的foreach并会实际上可以在任何IEnumerable<KeyValuePair<decimal, long>>(而不仅仅是SortedDictionary<decimal, long>)上工作,并且如果需要,很容易扩展到两个以上的输入集合。

不幸的是,它也完全忽视了这样一个事实,即输入SortedDictionary很好地被排序,因此性能不是最优的。为了获得最佳性能,您必须为每个输入的已排序词典提供线性前进单独IEnumerator,同时不断比较基本元素 - 您可以完全避免TryGetValue这种方式...