2012-08-27 39 views
2

作为一名python新手,我需要解决这个非常简单的问题。 说我有一个类:Python,从列表中删除重复项并将其转换为one2many

class Event(): 
    eid = 0 
    events = [] 

    def __repr__(self): 
    return "id:"+str(self.eid) + "=>" + str(self.events) 

    def __str__(self): 
    return self.__repr__() 

让我们创造出一些实例,并将它们保存到一个列表

eventset = list() 
e1 = Event() 
e1.eid = 0 
e1.events = [('1','2','3','A')] 

e3 = Event() 
e3.eid = 1 
e3.events = [('4','5','6','A')] 

e2 = Event() 
e2.eid = 0 
e2.events = [('7','8','9','A')] 

e4 = Event() 
e4.eid = 1 
e4.events = [('10','11','12','A')] 

eventset.append(e1,e2,e3,e4) 

打印EventSet这样得到:

[id:0=>[('1', '2', '3', 'A')], id:0=>[('7', '8', '9', 'A')], id:1=>[('4', '5', '6', 'A')], id:1=>[('10', '11', '12', 'A')]] 

我想创建一个新的列表,它将如下所示:

[id:0=>[('1', '2', '3', 'A'),('7', '8', '9', 'A')], id:1=>[('4', '5', '6','A'),('10', '11', '12', 'A')]] 

如何做到这一点优雅的“Pythonic方式”?

编辑:

  1. 需要保留在名单上事件要素的顺序

  2. 不希望创建新的事件实例复制

+0

所以你想让你的班级有两种不同的存储方式吗?你知道如果你将一个变量赋值给一个对象,那是对同一个对象的引用吧?所以'a = b'为'a是b'赋予了'True'。它让我觉得你希望'eid'状态隐含在一个对象实例中,而不是api的一部分,但是我可能会误解你想要做的事情。 –

+2

您应该将'eid'和'events'属性的定义移动到'def __init __(self):'方法中。在这种情况下不会导致问题,但是您的'events'列表是在类的所有实例之间共享的同一个对象(直到您通过执行'e1.events = []'来替换列表)。如果你使用了'e1.events.append(...)'或类似的函数 – dbr

+0

'def __str __(self):return self .__ repr __()'可以更简单地写为'__str__ = __repr__', –

回答

2

你真正需要的是一个字典的关键是eid和项目都是你的事件。我从集合中使用defaultdict为字典提供了一个默认项目 - 在本例中是一个列表。

from collections import defaultdict 

d = defaultdict(list) 

for i in [e1,e2,e3,e4]: 
    d[i.eid].append(i.events[0]) 
+1

此外,我没有看到做出包含一个元组的列表的原因。 @Zdanozdan可能应该省略例如括号中的括号。 'e1.events = [('1','2','3','A')]''。 – TerjeR

+0

当然,这仅仅是一个例子,会有更多的 – Zdanozdan

+0

也许因为每个活动都有不同的房间/参加者,这可能是有用的 - 但你可能是正确的疏忽。 –

0

@Burkan Khalid的解决方案是最简单的。

被看中,你可以改变输出字典d为事件的另一个列表:

grouped_events = [] 
for (i, v) in d: 
    e = Event() 
    e.eid = i 
    e.events = v 
    grouped_events.append(e) 

当然,如果你的Event类有一个适当的__init__采取eidevents为论据可以简化...

grouped_events = [Event(i,v) for (i,v) in d.items()] 
+0

已经有事件实例,不想再创建它 – Zdanozdan

+0

我只是指(i)在类的定义中缺少'__init__'(ii)创建** new **'Events'几个预定义的“事件”的组合。 –

+0

无论如何,好的清单组合 – Zdanozdan

2

我建议你 “升级” Event类:

class Event(object): # <-- one change 
    eid = 0 
    events = [] 

    def __init__(self, eid=0, events=None): # <-- second change 
     self.eid = eid 
     if events is not None: self.events = list(events) 

    def __repr__(self): 
     return "id:"+str(self.eid) + "=>" + str(self.events) 

    def __str__(self): 
     return self.__repr__() 

下一页:

from operator import add, attrgetter 
from itertools import starmap, groupby 

merge_event = lambda e, events: Event(e, reduce(add, map(attrgetter("events"), events), [])) 
list(starmap(merge_event, groupby([e1,e2,e3,e4], attrgetter("eid")))) 

这是怎么回事这里

groupby返回迭代器的元组的列表:(keyvalues):

>>> list(groupby([e1,e2,e3,e4], attrgetter("eid"))) 
[(0, <itertools._grouper object at 0x105d96bd0>), (1, <itertools._grouper object at 0x105d96f10>)] 

其中key是你的分组标准和values是匹配项目的迭代器。在此代码中key = eid属性(attrgetter("eid"))和values =具有相同eid值的所有项目。

starmap与通用map的作用相同,但是:a)返回迭代器而不是列表,b)使用单独的参数调用给定的回调函数(f(*(key,value)) = f(key, values))。我们创建了特殊功能merge_event以与groupby输出一起操作。

merge_event以(keyvalues)元组为参数并产生一个Event对象。用key(实际上是eid)一切都很清楚。要创建事件列表,我使用reduce函数和add运算符(来自operator模块的函数表示)。它的工作原理是这样的:

>>> reduce(add, [[1,2,3], ["A","B","C"]], []) 
[1, 2, 3, 'A', 'B', 'C'] 

最后,map(attrgetter("events"), events)聚集在一起Event对象只针对events属性(这是事件的列表)值的列表。

+0

+1使用itertools,但对于初学者来说可能太过消化。也许你应该更新它,并解释发生了什么。 –

+0

更新回答采用逐步说明 –

+0

@AlexeyKachayev那!我喜欢。如何用事件集替换[e1,e2,e3,e4]? – Zdanozdan

0

所以我觉得我发现相当不错,相当优雅的解决方案。请看看,并commet /简化。

我创建了一个迭代器,只有当这个eid还没有被返回时,它才会返回带有eid的元素。

class first_unique_iter(object): 
    def __init__(self, mylist): 
    self.eventset = mylist 
    self.i = iter(mylist) 
    self.used = [] 

    def __iter__(self): 
    return self 

    def next(self): 
    element = self.i.next() 
    if element.eid not in self.used: 
     self.used.append(element.eid) 
     return element 
    else: 
     return self.next() 

然后是逻辑:

def slice_by_id(event, eventset): 
    return [e for e in eventset if e.eid == event.eid] 

def reduce_2one(x,y): 
    x.events.extend(y.events) 
    return x 

final = [reduce(reduce_2one, slice_by_id(event,eventset)) for event in first_unique_iter(eventset)] 

因此,对于具有独特的EID每月的第一个事件发现我们运行使用这个新的迭代器列表比较。有每个我们需要追加事件列表的事件列表中的相同eid的事件。这是在由eid列表切片调用的reduce()函数中完成的。

print final 
>>> [id:0=>[('1', '2', '3', 'A'), ('7', '8', '9', 'A')], id:1=>[('4', '5', '6', 'A'), ('10', '11', '12', 'A')]] 

难道你会想这个吗?