Suma de los valores en la lista cuando se cumple la condición

votos
1

Tengo una lista de Python lque contiene las instancias de la clase Element:

class Element:
    def __init__(self, id, value):
        self.id = id
        self.value = value

l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]

Ahora quiero resumir todos los valuemiembros de las clases Elementssi su ides igual para obtener esta lista:

l = [Element(1, 300), Element(2, 1), Element(3, 8)]

¿Cuál es la forma más Pythonic para hacer esto?

Publicado el 19/12/2018 a las 14:11
fuente por usuario
En otros idiomas...                            


3 respuestas

votos
6

No es (casi?) Nada de lo que itertoolsno puede hacer. Echar un vistazo a groupby:

from itertools import groupby
from operator import attrgetter


class Element:
    def __init__(self, id, value):
        self.id = id
        self.value = value
    def __repr__(self):  # kudos @mesejo
        return "Element({}, {})".format(self.id, self.value)

l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]

l.sort(key=attrgetter('id'))  # if it is already sorted by 'id', comment-out

res = [Element(g, sum(sub.value for sub in k)) for g, k in groupby(l, key=attrgetter('id'))]

lo que resulta en:

print(res)   # [Element(1, 300), Element(2, 1), Element(3, 8)]
Respondida el 19/12/2018 a las 14:17
fuente por usuario

votos
2

Una forma sería la creación de una defaultdictque mapea los identificadores de sumas de valores. Entonces podemos tomar esos resultados y utilizarlos para construir una nueva lista de Elements. Una forma de hacerlo es utilizar starmappara mapear los artículos de ese diccionario a los argumentos aElement

from collections import defaultdict
from itertools import starmap

class Element:
    def __init__(self, id, value):
        self.id = id
        self.value = value
    def __repr__(self):
        return "Element({}, {})".format(self.id, self.value)

l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]

d = defaultdict(int)

for e in l:
    d[e.id] += e.value

print(list(starmap(Element, d.items())))
# [Element(1, 300), Element(2, 1), Element(3, 8)]
Respondida el 19/12/2018 a las 14:18
fuente por usuario

votos
1

También puede obtener el resultado deseado usando setpara obtener sólo los identificadores únicos y sumal total de los valores. Por ejemplo:

class Element:
    def __init__(self, id, value):
        self.id = id
        self.value = value

l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]

ids = set(elem.id for elem in l)
totals = [Element(i, sum(elem.value for elem in l if elem.id == i)) for i in ids]
# [Element(1, 300), Element(2, 1), Element(3, 8)]
Respondida el 19/12/2018 a las 14:36
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more