从IDictionary<int, IEnumerable<int>>创建ILookup<int,int>

4

有没有一种优雅的方法将 IDictionary<int,IEnumerable<int>> 转换为 ILookup<int,int>?据我所知,它们应该是相同的,但我认为查找表更清晰。

背后的故事更加复杂,但我被迫选择具有其相关 id 列表的 id 列表:

masters
    .Select(m => new {masterId = m.Id, childIds = m.Children.Select(c => c.Id)})
    .ToDictionary(k => masterId, v => v.childIds)

我很乐意直接选择查找功能,但我不知道是否可能。
主变量类型的示例可以简单地如下:
public class Master
{
     public int Id { get; set; }
     public List<Master> Children { get; set; }
}

1
“ToLookup” 用于将平面列表转换为组 - 您已经拥有了组,那么您为什么需要查找?您需要什么样的“查找”,而您的“字典”又不能提供给您什么? - D Stanley
1
你是否在考虑像这样的代码?masters.ToLookup(m => m.Id, m => m.Children.Select(c => c.Id)); - Dennis
2
@Chips_100 这将创建一个从id到子id的可枚举枚举的查找。 - poke
1
那么答案很简单:不行,没有办法在它们之间进行转换。尽管在使用时非常相似,但它们在根本上是不同的。如果您想将字典转换为查找表,您必须将其展平,然后再次分组,这将丢弃有关索引的所有信息并要求重新构建它。这意味着性能不佳。 - poke
2
你是否考虑过创建一个包装类,将其包装在字典周围,并在其上实现查找接口? - Lasse V. Karlsen
显示剩余3条评论
4个回答

3

正如Lasse V. Karlsen在评论中建议的那样,您可以创建一个包装类型,该类型公开了一个ILookup

public class LookupDictionary<TKey, TElement> : ILookup<TKey, TElement>
{
    private readonly IDictionary<TKey, IEnumerable<TElement>> _dic;

    public LookupDictionary(IDictionary<TKey, IEnumerable<TElement>> dic)
    {
        _dic = dic;
    }

    public int Count
    {
        get { return _dic.Values.Sum(x => x.Count()); }
    }

    public IEnumerable<TElement> this[TKey key]
    {
        get { return _dic.ContainsKey(key) ? _dic[key] : Enumerable.Empty<TElement>(); }
    }

    public bool Contains(TKey key)
    {
        return _dic.ContainsKey(key);
    }

    public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
    {
        return _dic.Select(kv => new LookupDictionaryGrouping(kv)).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    class LookupDictionaryGrouping : IGrouping<TKey, TElement>
    {
        private KeyValuePair<TKey, IEnumerable<TElement>> _kvp;

        public TKey Key
        {
            get { return _kvp.Key; }
        }

        public IEnumerator<TElement> GetEnumerator()
        {
            return _kvp.Value.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public LookupDictionaryGrouping(KeyValuePair<TKey, IEnumerable<TElement>> kvp)
        {
            _kvp = kvp;
        }
    }
}

我的意思是:Lookups和Dictionaries of Lists之间最大的区别在于,当你请求一个不存在的键时,Lookups返回一个空的可枚举对象而不是抛出异常。这个实现没有遵守这个契约。 - afrazier
@afrazier 我之前不知道这一点,看了ILookup文档后发现它并没有包含这一部分,而是Lookup类型的一个实现细节。不过,当然可以很容易地添加进去。 - poke
1
Lookup<TKey, TElement>.Item如果在集合中未找到该键,则返回空序列。 编辑:我看到接口文档并没有指定这一点。奇怪。 - afrazier
DictionaryLookup 是一个更好的名称。 - acelent

1

好的,你可以将字典平整化,然后转换为Lookup


dict.SelectMany(kvp -> kvp.Value, (kvp, v) => new {k = kvp.Key, v})
    .ToLookup(kvp => kvp.k, kvp => kvp.v)

但实际上它与字典几乎相同,因此似乎是不必要的。

1
如果我理解正确,您想要将您的集合扁平化。您可以这样做:
masters.SelectMany(x => x.Children, (x, y) 
   => new { 
            ParentId = x.Id, 
            ChildId = y.Id 
          })
   .ToLookup(x => x.ParentId, y => y.ChildId);

所以你会得到你的 ILookup<int,int>。而且,你不需要任何 Dictionary 集合。但使用 Dictionary 也很安全。

0
你可以这样做 - 比纯lambda更易读... :)
Dictionary<int, IEnumerable<int>> dict = new Dictionary<int, IEnumerable<int>>();

dict.Add(1, new int[] {1, 2, 3});
dict.Add(2, new int[] {4, 5, 6});
dict.Add(3, new int[] {4, 5, 6});

var lookup = (from kv in dict
            from v in kv.Value
            select new KeyValuePair<int, int>(kv.Key, v)).ToLookup(k=>k.Key, v=>v.Value);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接