【问题标题】:c# linq combine 2 dictionaries时间:2019-05-01 标签:c#linq combine 2 dictionary
【发布时间】:2016-10-25 11:51:42
【问题描述】:

我有两个Dictionary<string, Item>s。 Item 有一个公共属性 int Level

我想组合这两个字典,其中键是唯一的,并且我希望能够在两者上指定级别。

类似

dictionary 1 = 所有级别 

我可以通过 foreach 循环轻松做到这一点。我也可以使用多个 linq 查询来做到这一点。 但是我似乎无法弄清楚如何进行这个单一的 linq 查询。

编辑-根据您的要求,约翰这里是 foreach 的代码

Dictionary<string, Dictionary<string, Item>> itemDictionary = new Dictionary<string, Dictionary<string, Item>>();

Dictionary<string, Item> items = new Dictionary<string,Item>();

            if (itemDictionary.ContainsKey(comboBox.Text))
            {
                foreach (KeyValuePair<string, Item> kvp in itemDictionary[comboBox.Text])
                {
                    if (!items.ContainsKey(kvp.Key) && kvp.Value.Level <= int.Parse(textBox.Text))
                        items.Add(kvp.Key, kvp.Value);
                }
            }

            if (itemDictionary.ContainsKey(comboBox1.Text))
            {
                foreach (KeyValuePair<string, Spell> kvp in itemDictionary[comboBox1.Text])
                {
                    if (!items.ContainsKey(kvp.Key) && kvp.Value.Level <= int.Parse(textBox1.Text))
                        items.Add(kvp.Key, kvp.Value);
                }
            }
            var query = from s in items
                        orderby s.Value.Level
                        select s;

            foreach (var i in query)
               listBox.Items.Add(string.Format("{0} {1}", i.Value.Level, i.Key));

【问题讨论】:

  • 所以你想要的结果是一个字典,其中包含字典 1 中的所有项目加上字典 2 中的所有项目,谁的键在字典 1 中不存在?
  • 当您说“组合”时,您的意思是添加到新字典中,还是将字典 2 中的元素插入字典 1?
  • @scmccart,听起来更像是 1 的 subset 和 2 的 subset
  • “在键是唯一的地方结合这两个字典”是什么意思?如果两个字典中有相同的键,你想发生什么?如果您可以使用 foreach 向我们展示您当前的代码,我们可能会帮助您...
  • Anthony 是正确的,我正在寻找两个字典的子集

标签: c# linq


【解决方案1】:

如果您可以轻松地在 foreach 循环中执行您想要的操作,那么您已经编写了代码。这告诉我,foreach 代码可能比复杂的 LINQ 查询更具可读性和更易于维护。

这听起来像是保留 foreach 循环代码的完美案例。 LINQ 可能更新,但这并不总是意味着它更好。

【讨论】:

  • +1 我讨厌调试 LINQ。它有它的时刻,但在某些情况下,foreach 会好很多
  • 如果我对 linq 的理解更好,那么可读性就成为一个值得商榷的点。我的目的是更好地掌握 linq 而不是产生答案。
【解决方案2】:

好的,该代码清楚地表明了您想要完成的任务。所以最终结果应该是一个字典,由两个字典中满足指定级别的项目组成。如果两个词典中都存在一个项目,则首选第一个词典中的项目。虽然可以在单个 Linq 查询中完成此操作,但您最终会重复一些工作。这是我想出的,如果您想轻松试用,它可以在 LinqPad 中运行。

var itemsOne = new[] {
    new { Name = "A", Level = 1 },
    new { Name = "B", Level = 2 },
    new { Name = "C", Level = 3 },
    new { Name = "D", Level = 4 }
}.ToDictionary(i => i.Name, i => i);

var itemsTwo = new[] {
    new { Name = "C", Level = 10 },
    new { Name = "D", Level = 20 },
    new { Name = "E", Level = 30 },
    new { Name = "F", Level = 40 }
}.ToDictionary(i => i.Name, i => i);

var itemsOneLevel = 3;
var itemsTwoLevel = 30;

var validFromItemsOne = (from item in itemsOne
                         where item.Value.Level <= itemsOneLevel
                         select item).ToDictionary(i => i.Key, i => i.Value);

var validFromItemsTwo = from item in itemsTwo
                        where item.Value.Level <= itemsTwoLevel
                            && !validFromItemsOne.ContainsKey(item.Key)
                        select item;

var items = validFromItemsOne
    .Concat(validFromItemsTwo)
    .ToDictionary(i => i.Key, i => i.Value);

【讨论】:

    【解决方案3】:

    如果我没猜错你是对的:你想拥有两个字典中都存在的记录。无论如何,我的示例可以根据您的特定需求进行定制。

    Dictionary<string, string> d1 = new Dictionary<string, string>(), d2 = new Dictionary<string, string>();
            var merged = d1
                .Join(d2, d1key => d1key.Key, d2key => d2key.Key,
                    (i1, i2) => new { Key = i1.Key, Value1 = i1.Value, Value2 = i2.Value })
                .ToDictionary(t => t.Key);
    

    Merged 将仅包含两个字典中都存在的项目,并且能够获取每个项目的数据,例如 merge["key"].Value1 和 merge["key"].Value2 例如,我使用了匿名类型(新 {})。因此,您将只能以相同的方法或在绑定中访问 Value1 和 Value2,因为它们无论如何都是动态的。 在其他情况下,您应该创建一个类型来存储组合结果。

    【讨论】:

    • d1 和 d2 将它们组合起来,以便 d3 = 唯一的项目。如果 d1 和 d2 都有一个 level 满足条件的值,则只添加 d1 值而忽略 d2 值。
    【解决方案4】:

    这是我认为适用于这种情况的扩展。

    public static class Extensions
    {
        public static IDictionary<TKey, TValue> Combine<TKey, TValue>(
            this IDictionary<TKey, TValue> firstSet,
            IDictionary<TKey, TValue> secondSet,
            Func<KeyValuePair<TKey, TValue>, bool> firstFilter,
            Func<KeyValuePair<TKey, TValue>, bool> secondFilter)
        {
            return firstSet
            .Where(firstFilter)
            .Concat(secondSet.Where(secondFilter))
            .GroupBy(d => d.Key)
            .ToDictionary(d => d.Key, d => d.First().Value);
        }
    }
    

    用法:

    var itemsOne = new[] {
        new { Name = "A", Level = 1 },
        new { Name = "B", Level = 2 },
        new { Name = "C", Level = 3 },
        new { Name = "D", Level = 4 }
    }.ToDictionary(i => i.Name, i => i);
    
    var itemsTwo = new[] {
        new { Name = "C", Level = 10 },
        new { Name = "D", Level = 20 },
        new { Name = "E", Level = 30 },
        new { Name = "F", Level = 40 }
    }.ToDictionary(i => i.Name, i => i);
    
    itemsOne
    .Combine(itemsTwo,
        kvp => kvp.Value.Level <= 3,
        kvp => kvp.Value.Level <= 30)
        .ToDictionary(d => d.Key, d=> d.Value.Level)
    

    结果:

    1 乙二 C 3 20 30

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多