[英]Find matching KVP from Dictionary<List<enum>,string> where search key is List<enum> and return reverse partial matches
我有一个字典,其中键是枚举值列表,值是一个简单的字符串。
我需要做的是使用另一个枚举值列表找到匹配KVP。
这里发布的曲线球和原因是,如果我的测试或搜索列表中的列表包含字典中任何键中的所有项(或枚举对象),我还需要它返回KVP。
示例摘录代码:
public enum fruit{ apple , orange , banana , grapes };
public class MyClass
{
public Dictionary<List<fruit>, string> FruitBaskets = new Dictionary<List<fruit>, string>;
FruitBaskets.Add(new List<fruit>{apple,orange},"Basket 1");
List<fruit> SearchList = new List<fruit>{orange,apple,grapes};
}
我需要在字典中搜索SearchList
并返回“Basket 1”。
请注意,匹配可能比您对此类示例的预期要落后,因为我需要将密钥再次匹配搜索列表,反之亦然,因此搜索列表中不在密钥中的额外项目是可以的。
我知道我可以简单地迭代dict并逐个检查,但我也需要尽可能快,因为它驻留在一个运行得相当快的循环中。
我目前使用的是;
public Dictionary<List<fruit>, string> SearchResults;
foreach (KeyValuePair<List<fruit>, string> FruitBasket in FruitBaskets)
{
if (FruitBasket.Key.Except(SearchList).Count() == 0)
SearchResults.Add(FruitBasket);
}
想知道是否有更好/更快的方式。
你需要重新考虑你在字典中选择键。 List键存在一些主要问题,例如:
您不能对List使用O(1)键查找
你的密钥不是一成不变的
您可以将相同的列表作为键而不会收到错误,例如,您可以:
var a = new[] { fruit.organge }.ToList(); var b = new[] { fruit.organge }.ToList(); fruitBasket.Add(a, "1"); fruitBasket.Add(b, "2");
但这本字典有效吗? 我猜不是,但这取决于你的要求。
因此,您需要更改字典键类型。 您可以使用组合的枚举值,而不是使用带有按位运算符的List。 为此,您需要为每个枚举值分配2的幂:
[Flags]
public Enum Fruit
{
Orange = 1,
Apple = 2,
Banana = 4,
Grape = 8
}
您必须组合这些枚举值以获得所需的多值枚举字典键效果:
对于[Fruit.Orange, Fruit.Apple]
你使用Fruit.Orange | Fruit.Apple
Fruit.Orange | Fruit.Apple
。
以下是组合和分解值的示例代码:
private static fruit GetKey(IEnumerable<fruit> fruits)
{
return fruits.Aggregate((x, y) => x |= y);
}
private static IEnumerable<fruit> GetFruits(fruit combo)
{
return Enum.GetValues(typeof(fruit)).Cast<int>().Where(x => ((int)combo & x) > 0).Cast<fruit>();
}
现在您需要一个函数来获取SearchList的所有组合(幂集):
private static IEnumerable<fruit> GetCombinations(IEnumerable<fruit> fruits)
{
return Enumerable.Range(0, 1 << fruits.Count())
.Select(mask => fruits.Where((x, i) => (mask & (1 << i)) > 0))
.Where(x=>x.Any())
.Select(x=> GetKey(x));
}
使用这些组合,您可以使用O(1)时间从字典中查找值。
var fruitBaskets = new Dictionary<fruit, string>();
fruitBaskets.Add(GetKey(new List<fruit> { fruit.apple, fruit.orange }), "Basket 1");
List<fruit> SearchList = new List<fruit> { fruit.orange, fruit.apple, fruit.grapes };
foreach (var f in GetCombinations(SearchList))
{
if (fruitBaskets.ContainsKey(f))
Console.WriteLine(fruitBaskets[f]);
}
考虑以不同的方式存储您的数据:
var FruitBaskets = Dictionary<fruit, List<string>>();
每个条目包含与至少一个水果匹配的元素。 从您的结构转换如下:
foreach (var kvp in WobblesFruitBaskets)
{
foreach (var f in kvp.Key)
{
List<string> value;
if (!FruitBaskets.TryGetValue(f, out value))
{
value = new List<string>();
FruitBaskets.Add(f, value);
}
value.Add(kvp.Value);
}
}
现在,搜索将如下所示:对于组合键searchList
,首先计算单个键的结果:
var partialResults = new Dictionary<fruit, List<string>>();
foreach (var key in searchList)
{
List<string> r;
if (FruitBaskets.TryGetValue(key, out r))
{
partialResults.Add(key, r);
}
}
现在,剩下的就是撰写所有可能的搜索结果。 这是最难的部分,我认为这是你的方法所固有的:对于具有n个元素的键,你有2 n - 1个可能的子键。 您可以从问题的答案中使用子集生成方法之一并生成最终结果:
var finalResults = new Dictionary<List<fruit>, List<string>>();
foreach (var subkey in GetAllSubsetsOf(searchList))
{
if (!subkey.Any())
{
continue; //I assume you don't want results for an empty key (hence "-1" above)
}
var conjunction = new HashSet<string>(partialResults[subkey.First()]);
foreach (var e in subkey.Skip(1))
{
conjunction.IntersectWith(partialResults[e]);
}
finalResults.Add(subkey, conjunction.ToList());
}
我已经改变了string
到List<string>
在结果的价值的一部分。 如果你的方法中有一些不变量保证总会只有一个结果,那么应该很容易解决这个问题。
如果你从Reference Type
创建一个Dictionary
,你只存储引用(非值),那么你不能只使用FruitBaskets[XXX]
(除了你使用你创建字典节点的相同键),你必须迭代你词典中的全部Keys
。
我认为这个功能很简单,对你有好处:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
if (Enumerable.SequenceEqual<fruit>(KEY,item))
return true;
}
return false;
}
和这个,
bool B = Contain(new List<fruit> { fruit.apple, fruit.orange }); //this is True
但是如果你想考虑成员的排列,你可以使用这个函数:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
HashSet<fruit> Hkey= new HashSet<fruit>(KEY);
if (Hkey.SetEquals(item))
return true;
}
return false;
}
这是输出:
bool B1 = Contain(new List<fruit> { fruit.orange, fruit.grapes }); // = False
bool B2 = Contain(new List<fruit> { fruit.orange, fruit.apple }); // = True
bool B3 = Contain(new List<fruit> { fruit.apple, fruit.orange }); // = True
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.