从Dictionary<;列表<;enum>;,字符串>;其中搜索关键字是List<;enum>
本文关键字:lt gt enum 搜索 关键字 List 字符串 列表 Dictionary | 更新日期: 2023-09-27 18:27:08
我有一个Dictionary,其中键是枚举值的列表,值是一个简单的字符串。
我需要做的是使用另一个枚举值列表来查找匹配的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);
}
想知道是否有更好/更快的方法。
您需要重新考虑字典中键的选择。列表键存在一些主要问题,例如:
-
您不能在列表中使用O(1)键查找
-
你的钥匙不是一成不变的
-
您可以将相同的列表作为密钥,而不会收到错误,例如,您可以有:
var a = new[] { fruit.organge }.ToList(); var b = new[] { fruit.organge }.ToList(); fruitBasket.Add(a, "1"); fruitBasket.Add(b, "2");
但是这本字典有效吗?我想不会,但这取决于你的要求。
- 您可以更改字典键
出于这个原因,您需要更改字典键类型。您可以使用组合的枚举值,而不是使用带有按位运算符的列表。为此,您需要为每个枚举值分配2的幂:
[Flags]
public Enum Fruit
{
Orange = 1,
Apple = 2,
Banana = 4,
Grape = 8
}
您必须组合这些枚举值才能获得所需的多值枚举字典密钥效果:
对于[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元素的键,您有2n-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
,您只存储了Reference(Not值),那么您不能简单地使用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