【问题标题】:Find dictionary items whose key matches a substring查找其键与子字符串匹配的字典项
【发布时间】:2012-05-16 02:08:59
【问题描述】:

我有一个像这样构造的大字典:

programs['New York'] = 'some values...' 
programs['Port Authority of New York'] = 'some values...' 
programs['New York City'] = 'some values...'
...

如何返回 programs 的所有元素,其键提到“纽约”(不区分大小写)?在上面的示例中,我想获取所有三个项目。

编辑:字典非常大,预计会随着时间的推移而变得更大。

【问题讨论】:

    标签: python dictionary substring key


    【解决方案1】:

    这通常称为宽松字典,可以使用后缀树有效地实现。

    这种方法使用的内存在键上是线性的,这是最优的,搜索时间在你正在搜索的子字符串长度上是线性的,这也是最优的。

    我在 python 中找到了这个库来实现这个。

    https://hkn.eecs.berkeley.edu/~dyoo/python/suffix_trees/

    【讨论】:

    【解决方案2】:

    你应该使用mensi给出的蛮力方法,直到它被证明太慢。

    这里有一些东西可以复制数据以加快查找速度。仅当您的搜索只针对整个单词时才有效 - 即您永远不需要匹配“New Yorks Best Bagels”,因为“york”和“yorks”是不同的词。

    words = {}
    for key in programs.keys():
        for w in key.split():
            w = w.lower()
            if w not in words:
                words[w] = set()
            words[w].add(key)
    
    
    def lookup(search_string, words, programs):
        result_keys = None
        for w in search_string.split():
            w = w.lower()
            if w not in words:
                return []
            result_keys = words[w] if result_keys is None else result_keys.intersection(words[w])
        return [programs[k] for k in result_keys]
    

    如果单词必须按顺序排列(即“York New”不应该匹配),您可以将暴力方法应用于result_keys 的短列表。

    【讨论】:

      【解决方案3】:

      您可以提前生成所有子字符串,并将它们映射到各自的键。

      #generates all substrings of s.
      def genSubstrings(s):
          #yield all substrings that contain the first character of the string
          for i in range(1, len(s)+1):
              yield s[:i]
          #yield all substrings that don't contain the first character
          if len(s) > 1:
              for j in genSubstrings(s[1:]):
                  yield j
      
      keys = ["New York", "Port Authority of New York", "New York City"]
      substrings = {}
      for key in keys:
          for substring in genSubstrings(key):
              if substring not in substrings:
                  substrings[substring] = []
              substrings[substring].append(key)
      

      然后您可以查询substrings 以获取包含该子字符串的键:

      >>>substrings["New York"]
      ['New York', 'Port Authority of New York', 'New York City']
      >>> substrings["of New York"]
      ['Port Authority of New York']
      

      优点:

      • 通过子字符串获取键与访问字典一样快。

      缺点:

      • 生成substrings 会在程序开始时产生一次性成本,所花费的时间与programs 中的键数成正比。
      • substrings 将随着 programs 中键的数量近似线性增长,从而增加脚本的内存使用量。
      • genSubstrings 具有与密钥大小相关的 O(n^2) 性能。例如,“纽约港务局”生成 351 个子字符串。

      【讨论】:

      • 感谢您的建议。当上面的 mensi 提到倒排索引时,我正在考虑这个问题。在项目的这一点上,我将不得不选择性能而不是内存使用。所以我也会对此进行测试。
      • 当性能成为问题时的绝佳答案。
      【解决方案4】:

      iteritems 和生成器表达式将执行此操作:

      d={'New York':'some values',
          'Port Authority of New York':'some more values',
          'New York City':'lots more values'}
      
      print list(v for k,v in d.iteritems() if 'new york' in k.lower())    
      

      输出:

      ['lots more values', 'some more values', 'some values']
      

      【讨论】:

        【解决方案5】:
        [value for key, value in programs.items() if 'new york' in key.lower()]
        

        【讨论】:

        • 没错。如果您的字典很大,请不要指望它会很快。
        • @MarkRansom 我正要补充一点,我的字典很大,预计会变得更大。到现在一直在做programs.get('new york'),速度非常快。
        • 如果遍历字典中的所有键对您的应用程序来说太慢,您需要构建一个针对此类查询的数据结构。那可能是某种基于单词的倒排索引或后缀树。
        • @mensi。谢谢。我现在正在做出改变,看看它的表现如何。我也会研究其他数据结构。
        • 这个答案也可以用任何条件包装,以获取字典中的任何键是否包含提供的子字符串/条件的布尔值:any([x for x in programs if 'new york' in x.lower()])
        猜你喜欢
        • 1970-01-01
        • 2019-05-01
        • 1970-01-01
        • 2014-06-08
        • 2021-12-28
        • 1970-01-01
        • 2017-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多