【问题标题】:Is it possible to do a partial string match on a Dictionary string key?是否可以对 Dictionary 字符串键进行部分字符串匹配?
【发布时间】:2011-10-19 04:02:04
【问题描述】:

我的代码中有一个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。

谁能推荐一种简洁的方法来实现这一点?或者提供更好的数据结构来检索这些信息?

【问题讨论】:

  • 我认为下面的各种答案表明人们对“子字符串”的含义有不同的假设。我从您对 11 的评论中假设您的意思是一个真正通用的子字符串,不一定是前缀、后缀,只是 [年|月|日],而不是正则表达式?
  • @J Trana - 你是对的,我的意思是一个真正通用的子字符串。
  • @LeopardSkinPillBoxHat,能否请您发布您采用的最终解决方案?
  • 这个功能应该很容易被 SortedDictionary 实现,但它仍然不是。 (?) Java 有NavigableMap

标签: c# dictionary containers


【解决方案1】:

我会做一个扩展方法:

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;
}

【讨论】:

  • 在第二次编辑中,如果您传递了适当的比较器方法,您可以有一个 int 类型的字典键,并说 43 部分匹配 343756。
【解决方案2】:

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

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

无论如何,您必须在所有键中搜索通用子字符串,而不需要一些非常酷的魔法(.Net 不提供),因此 LINQ 在这里应该不会对您造成太大伤害。

【讨论】:

    【解决方案3】:

    如果 Dictionary 使用内部散列,那么你就不走运了,因为相似的字符串会产生不同的散列。我刚刚在周末用 C 语言实现了这个要求的解决方案,一个面试测试/家庭作业。我使用排序数组作为底层结构 - 昂贵的插入,但快速查找(使用二进制搜索)。要查找键以前缀开头的所有条目,我会找到第一个,然后继续下一步,下一个...对于一般子字符串,即不仅是前缀,我的解决方案将不起作用。目前我不知道对“一般子字符串”搜索有什么建议。

    【讨论】:

      【解决方案4】:

      您可以拥有三个字典。年月日。

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

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

      注意,这样做不会产生最快的执行代码。

      【讨论】:

      • 我认为使用树会比这更快。如果您共享子节点,那么您不必做 linq 的事情。
      • 你不是必须多次遍历树来收集匹配的节点吗?这必须加起来。
      【解决方案5】:

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

      例如:

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

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

      地图["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.
      
      }
      

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

      【讨论】:

      • 你的提议对我的第二个例子没有帮助 - 我希望能够提供任何子字符串 - 它不一定需要在年/月/日被整齐地打破。
      • 如果你想要年、月、日,你将需要多级字典。
      猜你喜欢
      • 2019-05-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-07
      • 1970-01-01
      • 1970-01-01
      • 2017-04-02
      • 2012-06-15
      • 1970-01-01
      相关资源
      最近更新 更多