【问题标题】:LINQ Convert Dictionary to LookupLINQ 将字典转换为查找
【发布时间】:2009-09-23 06:07:39
【问题描述】:

我有一个Dictionary<MyType, List<MyOtherType>>类型的变量
我想将其转换为Lookup<MyType, MyOtehrType>

我想先使用 Lambda 函数,将字典展平,然后使用 ToLookup() 将其转换为 Lookup。我被字典卡住了。我考虑过使用 SelectMany 但无法正常工作。有人知道怎么做吗?

【问题讨论】:

    标签: linq dictionary lookup


    【解决方案1】:

    与 Jon 的方法相同,但避免创建匿名类型:

    var lookup = dictionary
                .SelectMany(p => p.Value, Tuple.Create)
                .ToLookup(p => p.Item1.Key, p => p.Item2);
    

    【讨论】:

    • 当你用答案击败 Jon Skeet 时应该奖励一个徽章;)
    • @3dGrabber "Beat the Skeet" 绝对应该是一个徽章。
    【解决方案2】:

    怎么样:

    var lookup = dictionary.SelectMany(pair => pair.Value,
                                       (pair, Value) => new { pair.Key, Value })
                           .ToLookup(pair => pair.Key, pair => pair.Value);
    

    当字典已经将所有信息正确分组时,这样做确实有点浪费,但我看不出一个简单的解决方法。当然,您可以自己实现ILookup<TKey, TValue>,并使用字典的包装器...

    【讨论】:

    • 就我而言,一个模块/类关注空列表的错误处理是没有意义的。当你有一个列表字典时,空列表有点异常,因为它可能会导致 KeyNotFoundException - ILookUp 接口提供更一致的行为。
    【解决方案3】:

    这里已经有一些答案了,但把它放在这里以供参考。 这会翻转带有值列表的字典,以将这些值作为查找列表的键。

    var myLookup = myDict.SelectMany(p => p.Value, 
            (pair, id) => Tuple.Create(id, pair.Key))
        .ToLookup(p => p.Item1, p => p.Item2);
    

    注释

    
    var myLookup = myDict.SelectMany(
            // specify that the select many is to be based off the Value which is a list of items
            p => p.Value, 
            // Using the individual items from that list, create a tuple of that item and the dictionary key it was under
            (pair, id) => Tuple.Create(id, pair.Key))
            // use the item as the lookup key, and put the original dictionary key (that
            // had that value underneath them) in the list of lookup values.
        .ToLookup(p => p.Item1, p => p.Item2);
    

    【讨论】:

      【解决方案4】:

      不是问题的答案,但我认为这是相关信息,应该在此处发布。

      您应该考虑一些边缘情况。所有这些都是关于字典的项目,它们有键,但没有值。

      这是预期的行为。为不同目的设计的字典和查找。

      var dic = new Dictionary<bool, IEnumerable<bool?>> { [true] = null };
      var lookup = dic.ToLookup();
      
      Assert.AreEqual(1, dic.Count);
      Assert.AreEqual(0, lookup.Count);
      
      Assert.IsTrue(dic.ContainsKey(true));
      Assert.IsFalse(lookup.Contains(true));
      
      Assert.IsFalse(dic.ContainsKey(false));
      Assert.IsFalse(lookup.Contains(false));
      
      dic[false] -> Exception
      lookup[false] -> bool?[0]
      

      【讨论】:

        【解决方案5】:

        迟到了,但我认为这应该可行,无需再次枚举所有内容并创建临时元组/匿名类型。

        public static ILookup<TKey, TElement> ToLookup<TKey, TElement>(
            this IEnumerable<TKey> keys,
            Func<TKey, IEnumerable<TElement>> selector)
        {
            return new ManualLookup<TKey, TElement>(keys, selector);
        }
        
        private class ManualLookup<TKey, TElement> : ILookup<TKey, TElement>
        {
            private IEnumerable<TKey> _keys;
            private Func<TKey, IEnumerable<TElement>> _selector;
        
            public ManualLookup(IEnumerable<TKey> keys, Func<TKey, IEnumerable<TElement>> selector)
            {
                _keys = keys;
                _selector = selector;
            }
        
            public IEnumerable<TElement> this[TKey key] => _selector(key);
        
            public int Count => _keys.Count();
        
            public bool Contains(TKey key) => _keys.Contains(key);
        
            public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() => _keys
                .Select(key => new ManualGrouping<TKey, TElement>(key, _selector(key)))
                .GetEnumerator();
        
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
        
        private class ManualGrouping<TKey, TElement> : IGrouping<TKey, TElement>
        {
            private TKey _key;
            private IEnumerable<TElement> _enumerable;
        
            public ManualGrouping(TKey key, IEnumerable<TElement> enumerable)
            {
                _key = key;
                _enumerable = enumerable;
            }
        
            public TKey Key => _key;
        
            public IEnumerator<TElement> GetEnumerator() => _enumerable.GetEnumerator();
        
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
        

        然后你可以这样做:

        Dictionary<MyType, List<MyOtherType>> dictionary;
        return dictionary.Keys.ToLookup(key => 
        {
            if (dictionary.TryGetValue(key, out var list)
            {
                return list;
            }
        
            return Enumerable.Empty<MyOtherType>();
        });
        

        【讨论】:

          猜你喜欢
          • 2010-10-31
          • 1970-01-01
          • 1970-01-01
          • 2019-09-15
          • 2011-06-20
          • 1970-01-01
          • 2013-07-29
          • 1970-01-01
          • 2011-09-19
          相关资源
          最近更新 更多