我有一个类型为 Dictionary<MyType, List<MyOtherType>>
的变量,
我想将它转换为 Lookup<MyType, MyOtherType>
。
我想要使用Lambda函数先将字典展开,然后使用 ToLookup()
进行转换。但是我卡在了字典上。我考虑使用 SelectMany
但无法使其工作。请问有人知道如何做吗?
我有一个类型为 Dictionary<MyType, List<MyOtherType>>
的变量,
我想将它转换为 Lookup<MyType, MyOtherType>
。
我想要使用Lambda函数先将字典展开,然后使用 ToLookup()
进行转换。但是我卡在了字典上。我考虑使用 SelectMany
但无法使其工作。请问有人知道如何做吗?
与Jon的方法相同,但避免创建匿名类型:
var lookup = dictionary
.SelectMany(p => p.Value, Tuple.Create)
.ToLookup(p => p.Item1.Key, p => p.Item2);
怎么样:
var lookup = dictionary.SelectMany(pair => pair.Value,
(pair, Value) => new { pair.Key, Value })
.ToLookup(pair => pair.Key, pair => pair.Value);
虽然字典已经将所有信息适当地分组,但这样做感觉有点浪费,但我看不到简单的解决方法。当然,你可以通过在字典周围包装一个实现ILookup<TKey, TValue>
接口的类来实现...
这里已经有一些答案了,但为了方便参考,我放在这里。这个代码块将一个包含值列表的字典反转,将这些值作为查找列表的键。
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);
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>();
});
虽然这不是回答问题的答案,但是我认为这是相关信息,应该在此发布。
你应该考虑一些特殊情况。其中所有与字典中具有键却没有值的项有关。
这是预期的行为。字典和查找的设计目的不同。
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]