2011-10-19 15 views
5

Ho un dict contenenti liste sotto i suoi tasti:Il modo migliore riconoscimento somiglianza lista lunghezze

dct = {'a': [1, 2, 3], 
     'b': [1, 2, 3, 4], 
     'c': [1, 2]} 

Qual è il modo migliore per riconoscere se la lunghezza delle liste sono stesso o no ?

Questa è la mia soluzione:

import itertools 
len(set(itertools.imap(len, dct.viewvalues()))) == 1 

True se simile e False se non

UPD: In riferimento al consiglio @RaymondHettinger sostituire map a itertools.imap

+5

Solo per chiarimenti: vuoi dire simile o uguale? – Andrej

+0

@Andrej ovviamente * lo stesso * – scraplesh

+0

Penso che tu intenda "valori" invece di "valori di vista". –

risposta

6

La vostra soluzione sembra a posto.

Se si vuole tweek un po ', usare itertools.imap() al posto dimappa(). Ciò collasserà l'impronta di memoria su O (1) anziché su O (n).

+0

grazie per un consiglio! – scraplesh

+0

cosa si può dire della soluzione con * espressione del generatore * sotto? – scraplesh

+1

Anche il genexp è bello e conferisce lo stesso risparmio di memoria di imap(), ma map/imap viene eseguito più velocemente perché la ricerca globale di len() viene eseguita una sola volta. –

2

Nota: ovgolovin's solution è molto meglio. Lascio questa risposta qui perché c'è una discussione che si riferisce ad essa.

La soluzione va bene, ma si potrebbe usare un generatore di espressione che usa meno memoria ed è più leggibile:

len(set(len(x) for x in dct.viewvalues()))) == 1 
+0

d'accordo con te, questa è una soluzione più leggibile – scraplesh

4

In primo luogo, vorrei bastone con itervalues, che utilizza facile valutazione.

In secondo luogo, sarei cauto nel fare affidamento sull'utilizzo di set poiché esegue la ricerca del valore nell'insieme a ogni iterazione del dizionario. È O(1) in caso di eccedenza (e O(n) nel caso peggiore che è O(1) nel nostro caso se tutte le lunghezze sono uguali e O(n) se la lunghezza è diversa) in base allo docs. Ma è difficile valutare il sovraccarico dell'utilizzo del set.

Vorrei utilizzare all in questo caso. all non riesce quando trova il primo valore False. Quindi, la prima mancata corrispondenza della lunghezza interromperebbe il processo di interazione. Mentre, se si utilizza set, passerebbe attraverso tutta la lista fino alla fine e solo allora confrontare la sua lunghezza a 1.

>>> dct = {'a': [1, 2, 3], 
     'b': [1, 2, 3, 4], 
     'c': [1, 2]} 
>>> lenght_1 = len(dct.itervalues().next()) 
>>> all(len(value)==lenght_1 for value in dct.itervalues()) 
False 

>>> dct = {'a': [1, 2, 3], 
     'b': [1, 2, 4], 
     'c': [1, 2, 5]} 
>>> lenght_1 = len(dct.itervalues().next()) 
>>> all(len(value)==lenght_1 for value in dct.itervalues()) 
True 

Il codice può essere ottimizzato utilizzando lo stesso iteratore it che non passerà attraverso il primo valore due volte:

>>> it = dct.itervalues() 
>>> length_1 = len(next(it)) 
>>> all(len(value)==l1 for value in it) 
True 
1

Come Michael J. Barber ha suggerito nei commenti a the answer, ecco il codice che utilizza groupby e imap dal modulo itertools.

imap applica la funzione len ad ogni elenco.

groupby solo spilla i valori in blocchi della stessa lunghezza.

Quindi, se c'è più di una porzione di lunghezza, le lunghezze sono diverse. Se esiste un solo chuck di lunghezze, significa che le lunghezze degli elenchi sono uguali e che il secondo accesso all'iteratore groupby deve produrre StopIteration restituendo None (il valore predefinito della funzione next).

Il grande vantaggio di questo codice è che imap e groupby sono scritti in C e sono piuttosto veloci.

from itertools import imap,groupby 

dct = {'a': [1, 2, 3], 
     'b': [1, 2, 3, 4], 
     'c': [1, 2]} 

dct2 = {'a': [1, 2, 3], 
     'b': [1, 2, 34], 
     'c': [1, 2, 5]} 

def check_lenghts(iterable): 
    it = groupby(imap(len,iterable.itervalues())) 
    next(it,None) 
    return True if next(it,None)==None else False 

print(check_lenghts(dct)) 
print(check_lenghts(dct2)) 
+0

Perché ho menzionato 'islice': puoi usare' islice' per limitare l'iteratore creato da 'groupby' ad avere una lunghezza di massimo due. Qui, puoi sostituire le chiamate 'next' in' check_lengths' con 'len (list (islice (it, 2)) <2'. Nel complesso, penso che l'altra tua risposta sia probabilmente più chiara. –

+0

@ MichaelJ.Barber Sull'altro mano, il 'groupby' andrà attraverso tutti i valori della seconda lunghezza prima di dare il secondo valore (e se la lista ha il primo valore di lunghezza 1 e il resto dice 1 milione di valori di lunghezza 2, il' groupby' passerà tutti i milioni di valori di lunghezza 2. Ma usando la funzione 'all' come nell'altra risposta si dovrà solo accedere al primo valore dell'elenco lunghezza-2 per restituire' False'. – ovgolovin

+0

@ MichaelJ.Barber Ho dato un'occhiata più da vicino al equivalente a 'groupby' in puro Python. Il valore è dato da' groupby' nel momento in cui viene raggiunto il nuovo valore di 'key'. Quindi, se il codice C reale porta lo stesso comportamento, allora non ci sarà passaggio alla fine di il pezzo di seconda lunghezza raggiunge così la stessa efficienza della versione con 'all'. – ovgolovin

Problemi correlati