2012-08-27 36 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')]] 

難道你會想這個嗎?