繁体   English   中英

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

[英]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键存在一些主要问题,例如:

  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. 你可以改变字典键!

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

我已经改变了stringList<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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM