2009-09-08 6 views
6

Je veux trier une liste de dictionnaires par clé de dictionnaire, où je ne veux pas faire la distinction entre les majuscules et minuscules.python: combine tri-clé-fonctions itemgetter et str.lower

dict1 = {'name':'peter','phone':'12355'} 
dict2 = {'name':'Paul','phone':'545435'} 
dict3 = {'name':'klaus','phone':'55345'} 
dict4 = {'name':'Krishna','phone':'12345'} 
dict5 = {'name':'Ali','phone':'53453'} 
dict6 = {'name':'Hans','phone':'765756'} 
list_of_dicts = [dict1,dict2,dict3,dict4,dict5,dict6] 

key_field = 'name' 
list_of_dicts.sort(key=itemgetter(key_field)) 
# how to combine key=itemgetter(key_field) and key=str.lower? 
for list_field in list_of_dicts: 
    print list_field[key_field] 

devrait fournir

Ali, Hans, klaus, Krishna, Paul, peter 

et non

klaus, peter, Ali, Hans, Krishna, Paul 

Répondre

10

Dans le cas général, vous Je veux écrire votre fonction d'extraction de clé pour le tri. Seulement dans des cas spéciaux (bien qu'importants), il vous arrive de réutiliser un callable existant pour extraire les clés pour vous, ou simplement d'en joindre quelques-unes (d'une manière "rapide et sale" en utilisant lambda, puisqu'il n'y a pas de de manière à faire la composition de la fonction).

Si vous avez souvent besoin d'effectuer ces deux types d'opérations d'extraction clés (obtenir un élément et appeler une méthode sur ce point), je propose:

def combiner(itemkey, methodname, *a, **k): 
    def keyextractor(container): 
    item = container[itemkey] 
    method = getattr(item, methodname) 
    return method(*a, **k) 
    return keyextractor 

si listofdicts.sort(key=combiner('name', 'lower')) fonctionnera dans votre cas. Notez que si la généralisation excessive a des coûts, une généralisation de bon goût et modérée (en laissant la clé de l'élément, le nom de la méthode et les arguments de la méthode, le cas échéant, en fonction du temps d'exécution) présente généralement des avantages. plus complexe qu'une douzaine de spécifiques et spécialisés (avec l'extracteur, la méthode à appeler, ou les deux, câblés dans leur code), seront plus faciles à maintenir (et, bien sûr, beaucoup plus faciles à réutiliser!).

2
def lower_getter(field): 
    def _getter(obj): 
     return obj[field].lower() 
    return _getter 

list_of_dicts.sort(key=lower_getter(key_field)) 
+0

et en plus, il fonctionnera automatiquement avec les deux chaînes d'octets et les chaînes unicode. – nosklo

12

Que diriez-vous ceci:

list_of_dicts.sort(key=lambda a: a['name'].lower()) 
+0

je reçois une erreur « non énumérables » – chovy

4

Vous devriez probablement aller avec un lambda par souci de lisibilité. Mais comme une étude intéressante dans les fonctions d'ordre supérieur, voici la version étendue de q-combinator en Python (également connu sous le nom de combinateur d'oiseaux queer). Cela vous permet de créer une nouvelle fonction en composant deux fonctions

def compose(inner_func, *outer_funcs): 
    if not outer_funcs: 
     return inner_func 
    outer_func = compose(*outer_funcs) 
    return lambda *args, **kwargs: outer_func(inner_func(*args, **kwargs)) 

from operator import itemgetter, methodcaller 
name_lowered = compose(itemgetter('name'), methodcaller('lower')) 
print(name_lowered({'name': 'Foo'})) 

Si vous inversez les définitions des internes et externes dans la fonction compose, vous obtenez le b-Combinator plus traditionnelle (Bluebird). J'aime le q-combinator plus en raison de la similitude avec les pipes unix.

4

Cette solution utilisera les paramètres régionaux de votre système et, en prime, elle triera également les autres caractères en fonction des paramètres régionaux actuels (mettra "ü" après "u" dans une langue allemande, etc.).

from locale import setlocale, strxfrm, LC_ALL 
import operator 

# call setlocale to init current locale 
setlocale(LC_ALL, "") 

def locale_keyfunc(keyfunc): 
    def locale_wrapper(obj): 
    return strxfrm(keyfunc(obj)) 
    return locale_wrapper 

list_of_dicts.sort(key=locale_keyfunc(operator.itemgetter("name"))) 

Bien sûr, utilise que le tri locale est l'interface utilisateur sorte « naturel » que vous souhaitez émuler avec .lower().

Je suis étonné que le module locale de python est inconnu et utilisé, il est certain est un élément important dans l'application write (traduit en plusieurs langues, mais le module locale est importante même pour obtenir un droit du module. Exemple: en suédois 'V' et 'W', il faut donc les assembler, locale fait tout ça pour vous. Dans les paramètres régionaux POSIX (pas par défaut), cela revient à trier "a" après "Z".

+0

C'est une bonne suggestion, il suffit de changer keyfunc à: def keyfunc (CIVD): strxfrm de retour (CIVD [ « nom »]) – Francesco

+0

Francesco: Maintenant, il utilise une usine plus personnalisable le style (bien qu'il puisse être spécialisé pour être plus rapide, cela compte rarement). – u0b34a0f6ae

4

Personnellement, je souhaite qu'il y avait deux fonctions dans la bibliothèque standard de Python (probablement functools):

def compose(*funcs): 
    """ 
    Compose any number of unary functions into a single unary 
    function. 

    >>> import textwrap 
    >>> str.strip(textwrap.dedent(compose.__doc__)) == compose(str.strip, textwrap.dedent)(compose.__doc__) 
    True 
    """ 

    compose_two = lambda f1, f2: lambda v: f1(f2(v)) 
    return reduce(compose_two, funcs) 

def method_caller(method_name, *args, **kwargs): 
    """ 
    Return a function that will call a named method on the 
    target object with optional positional and keyword 
    arguments. 

    >>> lower = method_caller('lower') 
    >>> lower('MyString') 
    'mystring' 
    """ 
    def call_method(target): 
     func = getattr(target, method_name) 
     return func(*args, **kwargs) 
    return call_method 

J'ai mis en œuvre ces derniers pour mon propre usage dans jaraco.util.functools. De toute façon, maintenant votre code est assez clair, auto-documenté et robuste (IMO).

lower = method_caller('lower') 
get_name = itemgetter('name') 
lowered_name = compose(lower, get_name) 

list_of_dicts.sort(key=lowered_name) 
3
from functools import partial 

def nested_funcs(*funcs): 
    return partial(reduce, lambda arg, func: func(arg), funcs) 


sorted(list_of_dicts, key=nested_funcs(itemgetter('name'), str.strip, str.lower))