从Dictionary <list <enum>,string&gt;中找到匹配的KVP其中搜索键是List <enum>并返回反向部分匹配</enum> </list <enum>

时间:2014-10-18 12:32:43

标签: c# search dictionary enums

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

我需要做的是使用另一个枚举值列表找到匹配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并返回&#34;购物篮1&#34;。

请注意,匹配可能会落后于您对此类示例所期望的匹配,因为我需要将密钥再次匹配搜索列表而不是相反,因此搜索列表中不在密钥中的额外项目是可以的

我知道我可以简单地迭代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);
}

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

3 个答案:

答案 0 :(得分:2)

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

  1. 您无法使用O(1)键查找列表

  2. 您的密钥不可变

  3. 您可以将相同的列表作为键而不会收到错误,例如您可以:

    var a = new[] { fruit.organge }.ToList();
    var b = new[] { fruit.organge }.ToList();
    fruitBasket.Add(a, "1");
    fruitBasket.Add(b, "2");
    
  4. 但这本字典有效吗?我猜不是,但这取决于你的要求。

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

答案 1 :(得分:0)

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

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 可能的子键。您可以使用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<string>。如果你的方法中有一些不变量保证总会只有一个结果,那么应该很容易解决这个问题。

答案 2 :(得分:0)

如果您从Dictionary创建Reference Type,则只存储参考(非值),那么您就不能使用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
相关问题