【问题标题】:c# merging multiple dictionaries into onec# 将多个字典合并为一个
【发布时间】:2017-05-23 16:13:46
【问题描述】:

首先,我想提一下,我几天前才开始学习C#,所以我对它的了解有限。

我正在将具有相同类型键值对的多个字典合并为一个字典。

以下是我的方法,它可以工作并且还可以处理重复项

 var result = dict1.Concat(dict2).GroupBy(d => d.Key)
              .ToDictionary(d => d.Key, d => d.First().Value);

            result = result.Concat(dict3).GroupBy(d => d.Key)
                .ToDictionary(d => d.Key, d => d.First().Value);

            result = result.Concat(dict4).GroupBy(d => d.Key)
                .ToDictionary(d => d.Key, d => d.First().Value);

            result = result.Concat(dict5).GroupBy(d => d.Key)
    .ToDictionary(d => d.Key, d => d.First().Value); 

我想知道是否有一种有效的方法来合并具有相同数据类型的键值对的多个字典。

【问题讨论】:

  • 你控制初始化吗?
  • 这看起来效率不高。此外,您希望如何处理 values 的覆盖似乎也没有明确的实现。我们是否假设dict5 优先于dict4,后者优先于dict3(等等)...?如果您可以帮助澄清,我们可以建议一些实现。
  • @Karolis 是的,我有 3 个不同的字典,我将它们合并到我的一种方法中以执行任务
  • @Tango 您是否可以简单地将所有条目添加到同一个字典中。您选择 5 个不同的词典有什么特别的原因吗?
  • @Karolis 我有 5 种不同的方法。每个方法都包含一个字典。我有一个主要方法可以处理来自每种方法的数据。我正在合并所有字典,以便我可以使用它来处理集体数据。

标签: c# dictionary


【解决方案1】:

由于字典实现了IEnumerable<KeyValuePair<TKey, TValue>>,你可以简单地写:

var result = dict1
    .Concat(dict2)
    .Concat(dict3)
    .Concat(dict4)
    .Concat(dict5)
    .ToDictionary(e => e.Key, e => e.Value);

这假设没有重复的键。

如果有重复的键,你可以得到每个键的第一个值

result = dict1
    .Concat(dict2)
    .Concat(dict3)
    .Concat(dict4)
    .Concat(dict5)
    .GroupBy(e => e.Key)
    .ToDictionary(g => g.Key, g => g.First().Value);

其他变体也是可以想象的,比如保持最大值/最小值等。

如果存在具有不同值的重复键,您还可以创建值列表字典

Dictionary<TKey, List<TValue>> result = dict1
    .Concat(dict2)
    .Concat(dict3)
    .Concat(dict4)
    .Concat(dict5)
    .GroupBy(e => e.Key, e => e.Value)
    .ToDictionary(g => g.Key, v => v.ToList());

您可以将它们插入到 HashSet&lt;T&gt; 中,而不是创建值的 List&lt;T&gt;,以仅保留唯一值。

如果重复键的值始终相同,则只需使用Union 而不是Concat:

var result = dict1
    .Union(dict2)
    .Union(dict3)
    .Union(dict4)
    .Union(dict5)
    .ToDictionary(e => e.Key, e => e.Value);

Union 产生两个序列的集合并集。 Concat 连接两个序列。

最后,您可以结合前面两种方法并丢弃相等的键/值对,但为每个键保留一个不同值的列表:

Dictionary<TKey, List<TValue>> result = dict1
    .Union(dict2)
    .Union(dict3)
    .Union(dict4)
    .Union(dict5)
    .GroupBy(e => e.Key, e => e.Value)
    .ToDictionary(g => g.Key, v => v.ToList());

这些示例表明,准确了解输入数据的形状(唯一/非唯一键和键值对)以及您期望的结果类型非常重要。


另一种方法是让您的不同方法返回列表或枚举而不是字典,并在最后将这些集合合并到字典中。这样会更有表现力。

【讨论】:

  • 可能有重复键。
  • 在这种情况下,你想用这些值做什么? (因为值可能不同,即使键相同,对吧?)
  • 没错,提到的键和它的值始终是相同的。
  • 如果我在调用.Union() 时收到异常声明An item with the same key has already been added,我是否做错了?我认为工会的全部意​​义在于为我处理这个问题。
  • 基于@jon-skeet 的评论,.Union() 方法也是 [愚蠢] 合并内容,如果包含集合,可能会导致重复键:stackoverflow.com/questions/4791931/…
【解决方案2】:

虽然它没有使用任何漂亮的Linq,但我认为以下会更有效率。它只创建一个额外的字典,即结果。它最初的大小是为了不会有增长。此外,插入的数量将恰好是结果字典中的元素数量。

我认为这将比创建多个中间字典或其他集合更有效,或者以导致新字典或中间字典必须经历多次增长调整的方式做事。在中间foreach,我不知道检查dict1 或result 是否更有效的是ContainsKey。我检查了dict1,因为没有必要检查result,它会从dict2 中获得越来越多的值,而且我们知道dict2 中的任何键都不会出现在dict2 中。

var result = new Dictionary<MyKeyType, MyValueType>(dict1.Count + dict2.Count + dict3.Count
    + dict4.Count + dict5.Count);
foreach(var pair in dict1) {
    result.Add(pair.Key, pair.Value);
}
foreach(var pair in dict2) {
    if (!dict1.ContainsKey(pair.Key)) result.Add(pair.Key, pair.Value);
}
foreach(var pair in dict3) {
    if (!result.ContainsKey(pair.Key)) result.Add(pair.Key, pair.Value);
}
foreach(var pair in dict4) {
    if (!result.ContainsKey(pair.Key)) result.Add(pair.Key, pair.Value);
}
foreach(var pair in dict5) {
    if (!result.ContainsKey(pair.Key)) result.Add(pair.Key, pair.Value);
}

在 5 个大字典的时间测试中,它们之间大部分是唯一的键,结果如下(以毫秒为单位):

  • 1037 毫秒为您的代码
  • 357 ms 用于使用 Linq 的另一个答案中的中间代码块
  • 784 ms 使用 Linq 在另一个答案中的第三个代码块
  • 43 ms 上面使用 foreach 的代码

如果键位于多个字典中,则第一个值是使用的值,因为您没有指定要处理这种情况的任何特定方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2021-07-14
    • 2011-05-04
    • 1970-01-01
    • 2020-02-14
    • 2014-09-02
    相关资源
    最近更新 更多