2012-01-29 10 views
5

Tengo dos listas de resultados de pruebas. Los resultados del ensayo se representan como diccionarios:Comparación de listas de diccionarios

list1 = [{testclass='classname', testname='testname', testtime='...},...] 
list2 = [{testclass='classname', testname='testname', ...},...] 

La representación diccionario es ligeramente diferente en ambas listas, porque, por una lista que tengo alguna más información. Pero en todos los casos, cada diccionario de prueba en cualquiera de las listas tendrá un nombre de clase y un elemento de nombre de prueba que juntos forman efectivamente una forma de identificar de forma única la prueba y una forma de compararla entre listas.

Necesito averiguar todas las pruebas que están en list1 pero no en list2, ya que representan nuevas fallas de prueba.

para hacer esto que hago:

def get_new_failures(list1, list2): 
    new_failures = [] 
    for test1 in list1: 
     for test2 in list2: 
      if test1['classname'] == test2['classname'] and \ 
        test1['testname'] == test2['testname']: 
       break; # Not new breakout of inner loop 
     # Doesn't match anything must be new 
     new_failures.append(test1); 
    return new_failures; 

me pregunto es una forma más pitón de hacer esto. Miré los filtros. La función que usa el filtro necesitaría manejar ambas listas. Una es fácil, pero no estoy seguro de cómo manejar ambas cosas. Sé el contenido de las listas hasta el tiempo de ejecución.

Cualquier ayuda sería apreciada,

Gracias.

+0

errores tipográficos @Wooble fijos – dublintech

Respuesta

8

Prueba esto:

def get_new_failures(list1, list2): 
    check = set([(d['classname'], d['testname']) for d in list2]) 
    return [d for d in list1 if (d['classname'], d['testname']) not in check] 
+1

+1, este fue el siguiente pensamiento que tuve, no me di cuenta de que lo había publicado. – senderle

2

Si cada combinación de classname y testname es realmente única, entonces el enfoque más eficiente computacionalmente sería usar dos diccionarios en lugar de dos listas. Como clave para el diccionario, use una tupla como la siguiente: (classname, testname). Entonces simplemente puede decir if (classname, testname) in d: ....

Si necesita conservar el orden de inserción, y está utilizando Python 2.7 o superior, puede usar un OrderedDict del módulo collections.

El código sería algo como esto:

tests1 = {('classname', 'testname'):{'testclass':'classname', 
            'testname':'testname',...}, 
     ...} 
tests2 = {('classname', 'testname'):{'testclass':'classname', 
            'testname':'testname',...}, 
     ...} 

new_failures = [t for t in tests1 if t not in tests2] 

Si tiene que usar listas, por alguna razón, usted podría iterar sobre list2 para generar un conjunto, y luego prueba para ser miembro de ese conjunto:

test1_tuples = ((d['classname'], d['testname']) for d in test1) 
test2_tuples = set((d['classname'], d['testname']) for d in test2) 
new_failures = [t for t in test1_tuples if t not in test2_tuples] 
+0

su código funciona muy bien, pero tiene un error tipográfico en la línea '' test2_tuples = ... - un paréntesis falta –

+0

@ ÓscarLópez, de hecho, gracias ! Corregido ahora. – senderle

2

para comparar dos dict d1 y d2 en un subconjunto de sus teclas, utilice:

all(d1[k] == d2[k] for k in ('testclass', 'testname')) 

Y si sus dos listas tienen la misma longitud, puede usar zip() para emparejarlas.

+3

Pero esto todavía requiere iteración sobre el producto cartesiano de las dos listas ... – senderle

+0

@senderle: Si tiene dos listas, no veo el problema en eso. –

Cuestiones relacionadas