web-dev-qa-db-fra.com

Rechercher des éléments de dictionnaire dont la clé correspond à une sous-chaîne

J'ai un grand dictionnaire construit comme suit:

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

Comment puis-je retourner tous les éléments de programs dont la clé mentionne "new york" (insensible à la casse)? Dans l'exemple ci-dessus, je voudrais obtenir les trois éléments.

EDIT: Le dictionnaire est assez volumineux et devrait grossir avec le temps.

34
Abid A
[value for key, value in programs.items() if 'new york' in key.lower()]
63
mensi

Ceci est généralement appelé un dictionnaire détendu et il peut être implémenté efficacement en utilisant un arbre de suffixes.

La mémoire utilisée par cette approche est linéaire sur les clés, ce qui est optimal, et le temps de recherche est linéaire sur la longueur de sous-chaîne que vous recherchez, qui est également optimale.

J'ai trouvé cette bibliothèque dans python qui implémente cela.

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

8
jordi

Vous devez utiliser la méthode de force brute donnée par mensi jusqu'à ce qu'elle se révèle trop lente.

Voici quelque chose qui duplique les données pour donner une recherche plus rapide. Cela ne fonctionne que si votre recherche porte uniquement sur des mots entiers - c'est-à-dire que vous n'aurez jamais besoin de faire correspondre les "meilleurs bagels de New York" car "york" et "yorks" sont des mots différents.

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]

Si les mots doivent être dans l'ordre (c.-à-d. "York New" ne doit pas correspondre), vous pouvez appliquer la méthode de force brute à la courte liste de result_keys.

5
Mark Ransom

Un iteritems et une expression de générateur feront ceci:

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())    

Sortie:

['lots more values', 'some more values', 'some values']
3
the wolf

Vous pouvez générer toutes les sous-chaînes à l'avance et les mapper à leurs clés respectives.

#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)

Ensuite, vous pouvez interroger substrings pour obtenir les clés qui contiennent cette sous-chaîne:

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

Avantages:

  • obtenir des clés par sous-chaîne est aussi rapide que d'accéder à un dictionnaire.

Les inconvénients:

  • La génération de substrings entraîne un coût unique au début de votre programme, prenant un temps proportionnel au nombre de clés dans programs.
  • substrings augmentera de façon approximativement linéaire avec le nombre de clés dans programs, augmentant l'utilisation de la mémoire de votre script.
  • genSubstrings a des performances O (n ^ 2) par rapport à la taille de votre clé. Par exemple, "Port Authority of New York" génère 351 sous-chaînes.
2
Kevin