是否可以对Dictionary字符串键进行部分字符串匹配?

时间:2011-10-19 04:02:05

标签: c# dictionary containers

我的代码中有一个Dictionary<string, List<int>>我正在使用以下方式:

Key           Values  
2011-07-15    1, 2, 3
2011-07-20    4, 5, 6
2010-02-11    7, 8, 9

我的代码需要能够查询与键中特定子字符串匹配的所有值。例如,如果我有子串2011-07,它应该返回值{1, 2, 3, 4, 5, 6}11的子字符串应返回1-9的所有ID。

有人可以推荐一种简洁的方法来实现这一目标吗?或者提供更好的数据结构来检索这些信息?

5 个答案:

答案 0 :(得分:9)

我会做一个扩展方法:

public static class DictionaryExt
{
    public static IEnumerable<T> PartialMatch<T>(this Dictionary<string, T> dictionary, string partialKey)
    {
        // This, or use a RegEx or whatever.
        IEnumerable<string> fullMatchingKeys = 
            dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey));

        List<T> returnedValues = new List<T>();

        foreach (string currentKey in fullMatchingKeys)
        {
            returnedValues.Add(dictionary[currentKey]);
        }

        return returnedValues;
    }
}

向字典中添加值的“成本”不会改变,但检索的成本会更高,但只有当您知道自己正在进行部分匹配时才会发生。

顺便说一句,我相信你可以用一个Lambda表达式来改变它,但概念保持不变。

编辑:在您的示例中,此方法将返回2个值列表,但您可以更改它以合并列表。以下是您可以执行的扩展方法:

public static IEnumerable<T> PartialMatch<T>(
    this Dictionary<string, IEnumerable<T>> dictionary,
    string partialKey)
{
    // This, or use a RegEx or whatever.
    IEnumerable<string> fullMatchingKeys = 
        dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey));

    List<T> returnedValues = new List<T>();

    foreach (string currentKey in fullMatchingKeys)
    {
        returnedValues.AddRange(dictionary[currentKey]);
    }

    return returnedValues;
}

编辑2 :想想看,你也可以使它更通用。使用下一个扩展方法,它可以在任何字典上工作,只要您提供comparer来检查“部分匹配”的含义:

public static IEnumerable<TValue> PartialMatch<TKey, TValue>(
    this Dictionary<TKey, IEnumerable<TValue>> dictionary,
    TKey partialKey,
    Func<TKey, TKey, bool> comparer)
{
    // This, or use a RegEx or whatever.
    IEnumerable<TKey> fullMatchingKeys = 
        dictionary.Keys.Where(currentKey => comparer(partialKey, currentKey));

    List<TValue> returnedValues = new List<TValue>();

    foreach (TKey currentKey in fullMatchingKeys)
    {
        returnedValues.AddRange(dictionary[currentKey]);
    }

    return returnedValues;
}

答案 1 :(得分:4)

您正在寻找简明的答案。如果没有花哨的文本索引(我不知道任何专门的.Net类),我认为词典仍然是你最好的选择。查询类似:

myDictionary.Where(kvp => kvp.Key.Contains("11")).SelectMany(kvp => kvp.Value);

你必须在所有键中搜索一个通用子字符串,而不需要一些非常酷的魔法(不是.Net提供的),所以LINQ在这里不应该伤害你。

答案 2 :(得分:2)

如果Dictionary使用内部哈希值,那么你运气不好,因为类似的字符串会产生不同的哈希值。我刚刚在周末用C,面试测试/家庭作业实施了这个要求的解决方案。我使用排序数组作为底层结构 - 昂贵的插入,但快速查找(使用二进制搜索)。要查找带有以前缀开头的密钥的所有条目,我会找到第1个,然后再进入下一个,下一个...对于一般子字符串,即不仅是前缀,我的解决方案不起作用。此刻我不知道为“一般子串”搜索建议什么。

答案 3 :(得分:2)

你可以有三本词典。年,月,日。

请注意,当您向三个词典添加项目时,您不会复制这些项目。

使用两个键拉出项目时,可以使用LINQ Extension方法Intersect()来获取与两个键匹配的项目(在两个结果集上使用Intersect)。

警告,这样做不会导致执行速度最快的代码。

答案 4 :(得分:1)

简洁的方法是使用多值映射。

例如:

Dictionary<string, Dictionary<string, List<int>>

为什么不将2011-07作为键存储,将15作为内部字典键,将1,2,3作为值存储。

map [“2011-07”] [“15”] = {1,2,3};

如果您只想要2011-07,您可以通过遍历获取其他字典中的所有内容。

map["2011-07"] //将返回你的1,2,3,4,5,6

如果你想去特定的一天,2011-07-15,这将只返回1,2,3

foreach(var element in map["2011-07"]){

     var values = element.values; // and you can append them to a list.

}

如果您需要年/月/日,则需要多级词典。或者您也可以使用