【问题标题】:Find matching KVP from Dictionary<List<enum>,string> where search key is List<enum> and return reverse partial matches从 Dictionary<List<enum>,string> 中查找匹配的 KVP,其中搜索键为 List<enum> 并返回反向部分匹配
【发布时间】:2014-12-13 21:46:29
【问题描述】:

我有一个字典,其中键是枚举值列表,值是一个简单的字符串。

我需要做的是使用另一个枚举值列表找到匹配的 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”。

请注意,对于此类示例,匹配可能比您期望的要向后匹配,因为我需要键来匹配搜索列表,反之亦然,因此搜索列表中不在键中的额外项目是可以的.

我知道我可以简单地迭代字典并一一检查,但我还需要它尽可能快,因为它驻留在运行相当快的循环中。

我目前使用的是;

public Dictionary<List<fruit>, string> SearchResults;
foreach (KeyValuePair<List<fruit>, string> FruitBasket in FruitBaskets)
{
    if (FruitBasket.Key.Except(SearchList).Count() == 0)
        SearchResults.Add(FruitBasket);
}

想知道是否有更好/更快的方法。

【问题讨论】:

    标签: c# search dictionary enums


    【解决方案1】:

    考虑以不同的方式存储您的数据:

    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 个可能的子键.您可以使用answers to this question 中的一种子集生成方法并生成最终结果:

    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&lt;string&gt;。如果您的方法中有一些不变量可以保证始终只有一个结果,那么修复它应该很容易。

    【讨论】:

    • 我想你明白我在问什么,但我很难相信这样重组我的数据不会对性能产生影响,因为搜索 List 中的值是不断变化的变化,这一切都发生在一个循环中。
    • @Wobbles 老实说,我也不确定。猜猜这取决于Except 的复杂性——对于良好的散列,它可能接近O(n),否则它是O(n^2)——以及你的搜索词中有多少元素。在 99.99999% 的情况下,唯一合理的知道方法是测量 :) 另一个让事情变得更快一点的想法是将枚举值编码为二进制,并使用位操作来比较键。
    • 你可能正在使用字节类型的枚举,我不得不玩弄它。
    • 您可以使用flag 属性标记枚举,以便对它们使用按位运算符msdn.microsoft.com/en-us/library/…
    • @Jason 巧合的是我的枚举已经是字节类型,它只是看看迭代字节比较器是否比我上面的更快除了列表比较器,我怀疑字节类型的比较器是更高效。
    【解决方案2】:

    如果您从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
    

    【讨论】:

    • 但这对于Contain(new List&lt;fruit&gt; { fruit.apple, fruit.orange, fruit.grapes }); 也会返回true 吗?这就是困难的部分,也是我最终需要的。
    【解决方案3】:

    您需要重新考虑选择字典中的键。 List 键存在一些主要问题,例如:

    1. 您不能对 List 使用 O(1) 键查找

    2. 您的密钥不是不可变的

    3. 您可以拥有与键相同的列表而不会收到错误,例如,您可以:

      var a = new[] { fruit.organge }.ToList();
      var b = new[] { fruit.organge }.ToList();
      fruitBasket.Add(a, "1");
      fruitBasket.Add(b, "2");
      

    但是这本词典有效吗?我猜不是,但这取决于您的要求。

    1. 您可以更改字典键!

    因此,您需要更改字典键类型。您可以使用组合的枚举值,而不是使用带有位运算符的列表。为此,您需要为每个枚举值分配 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]);
    }
    

    【讨论】:

      猜你喜欢
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-18
      • 2014-03-23
      相关资源
      最近更新 更多