2011-07-06 35 views
18

dado una lista¿Cómo agrupar una lista de tuplas/objetos por índice/atributo similar en python?

old_list = [obj_1, obj_2, obj_3, ...] 

Quiero crear una lista:

new_list = [[obj_1, obj_2], [obj_3], ...] 

donde obj_1.some_attr == obj_2.some_attr.

Podría lanzar algunos for bucles y if cheques juntos, pero esto es feo. ¿Hay una forma pitonica para esto? por cierto, los atributos de los objetos son todas las cadenas.

También se agradece la solución para una lista que contiene tuplas (de la misma longitud) en lugar de objetos.

+0

_ "una lista que contiene tuplas (de la misma longitud) en lugar de objetos" _ ¿Esto significa ** una lista que contiene tuplas todas de la misma longitud **? En caso afirmativo, ¿cuál es el "atributo" en el que se agrupan las tuplas? - Por cierto, las tuplas son objetos, ¿verdad? – eyquem

+0

@eyquem: 1. Sí; 2. las tuplas se agrupan en un cierto índice. El elemento en el índice es una cadena. 3. Creo que sí, pero no estoy seguro.:-) – Aufwind

Respuesta

31

defaultdict es cómo se hace esto.

Mientras que for bucles son en gran parte esenciales, las declaraciones if no lo son.

from collections import defaultdict 


groups = defaultdict(list) 

for obj in old_list: 
    groups[obj.some_attr].append(obj) 

new_list = groups.values() 
+3

Esto, por supuesto, no conserva (o respeta en modo alguno) el orden original de los grupos. Por lo tanto, puede o no ser lo que quería el @Druss. – tjollans

+1

@ jollybox.de: "no conserva (ni respeta en modo alguno) el orden original de los grupos" Correcto. ¿Cuándo se convirtió eso en un requisito? –

+0

No sé si es un requisito, la pregunta original no está clara al respecto. Originalmente leí la pregunta de esa manera. Aún así, buena respuesta. – tjollans

11

Creo que también puede intentar utilizar itertools.groupby. Tenga en cuenta que el siguiente código es solo una muestra y debe modificarse según sus necesidades:

data = [[1,2,3],[3,2,3],[1,1,1],[7,8,9],[7,7,9]] 

from itertools import groupby 

# for example if you need to get data grouped by each third element you can use the following code 
res = [list(v) for l,v in groupby(sorted(data, key=lambda x:x[2]), lambda x: x[2])]# use third element for grouping 
+1

Básicamente mi respuesta, pero olvidaste un aspecto importante: ordenar antes de usar 'groupby'. – JAB

+1

@JAB - tu verdad. Gracias por verme –

18

Aquí hay dos casos. Ambos requieren las siguientes importaciones:

import itertools 
import operator 

Usted va a utilizar y, o bien itertools.groupbyoperator.attrgetter o operator.itemgetter.

Para una situación en la que estés agrupación por obj_1.some_attr == obj_2.some_attr:

get_attr = operator.attrgetter('some_attr') 
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_attr), get_attr)] 

Para a[some_index] == b[some_index]:

get_item = operator.itemgetter(some_index) 
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_item), get_item)] 

en cuenta que necesita la clasificación porque itertools.groupby hace un nuevo grupo cuando el valor de la clave cambios.


Tenga en cuenta que puede usar esto para crear un dict como respuesta de S. Lott, pero no tiene que utilizar collections.defaultdict.

El uso de un diccionario por comprensión (sólo funciona con Python 3 +, y posiblemente Python 2.7, pero no estoy seguro):

groupdict = {k: g for k, g in itertools.groupby(sorted_list, keyfunction)} 

Para versiones anteriores de Python, o como una alternativa más sucinta:

groupdict = dict(itertools.groupby(sorted_list, keyfunction)) 
Cuestiones relacionadas