[英]Remove dictionary from list
如果我有字典列表,請說:
[{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]
我想刪除id
為 2 (或名稱'john'
)的字典,以編程方式解決這個問題的最有效方法是 go (也就是說,我不知道列表中條目的索引所以它不能簡單地彈出)。
thelist[:] = [d for d in thelist if d.get('id') != 2]
編輯:因為在關於此代碼性能的評論中表達了一些疑問(一些基於誤解 Python 的性能特征,一些基於假設超出給定規范列表中恰好有一個值為 2 的 dict 為 key ' id'),我想在這一點上保證。
在舊的 Linux 機器上,測量此代碼:
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 82.3 usec per loop
其中 random.shuffle 大約需要 57 微秒(需要確保要刪除的元素不總是在同一個位置;-)初始副本需要 0.65 微秒(擔心 Python 列表淺拷貝對性能影響的人最顯然是出去吃午飯;-),需要避免改變循環中的原始列表(因此循環的每一段確實都有要刪除的內容;-)。
當知道只有一個項目要移除時,可以更快地定位和移除它:
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); where=(i for i,d in enumerate(thelist) if d.get('id')==2).next(); del thelist[where]"
10000 loops, best of 3: 72.8 usec per loop
(當然,如果您使用的是 Python 2.6 或更高版本,則使用next
內置函數而不是.next
方法)——但是如果滿足刪除條件的字典數量不完全是一個,則此代碼會崩潰。 概括這一點,我們有:
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
10000 loops, best of 3: 23.7 usec per loop
可以刪除改組的地方,因為我們知道,已經有三個等距的 dict 需要刪除。 而 listcomp 沒有變化,表現良好:
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 23.8 usec per loop
完全脖子和脖子,甚至只有 99 的 3 個元素要刪除。 對於更長的列表和更多的重復,這當然更適用:
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
1000 loops, best of 3: 1.11 msec per loop
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
1000 loops, best of 3: 998 usec per loop
總而言之,與完全簡單明了的列表推導相比,顯然不值得部署制作和反轉要刪除的索引列表的微妙之處,以在一個小案例中可能獲得 100 納秒——而在更大的案例中失去 113 微秒;-) 避免或批評簡單、直接和完美的性能足夠的解決方案(例如此類“從列表中刪除某些項目”問題的列表推導式)是 Knuth 和 Hoare 著名論文的一個特別討厭的例子,即“過早的優化是編程中萬惡之源”!-)
這是一種通過列表理解來實現的方法(假設您將列表命名為“foo”):
[x for x in foo if not (2 == x.get('id'))]
替換'john' == x.get('name')
或任何適當的。
filter
也有效:
foo.filter(lambda x: x.get('id')!=2, foo)
如果你想要一個生成器,你可以使用 itertools:
itertools.ifilter(lambda x: x.get('id')!=2, foo)
但是,從 Python 3 開始, filter
無論如何都會返回一個迭代器,因此正如 Alex 所建議的那樣,列表推導式確實是最佳選擇。
這不是一個正確的 anwser(因為我認為你已經有一些很好的),但是......你有沒有考慮過使用<id>:<name>
的字典而不是字典列表?
# assume ls contains your list
for i in range(len(ls)):
if ls[i]['id'] == 2:
del ls[i]
break
平均而言可能比列表理解方法更快,因為如果它在早期找到有問題的項目,它不會遍歷整個列表。
您可以嘗試以下操作:
a = [{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]
for e in range(len(a) - 1, -1, -1):
if a[e]['id'] == 2:
a.pop(e)
如果你不能從頭彈出 - 從末尾彈出,它不會破壞 for 循環。
假設您的 python 版本是 3.6 或更高版本,並且您不需要刪除的項目,這會更便宜...
如果列表中的字典是唯一的:
for i in range(len(dicts)):
if dicts[i].get('id') == 2:
del dicts[i]
break
如果要刪除所有匹配的項目:
for i in range(len(dicts)):
if dicts[i].get('id') == 2:
del dicts[i]
您也可以這樣做以確保無論 python 版本如何,獲取 id 密鑰都不會引發 keyerror
如果 dicts[i].get('id', None) == 2
您可以嘗試以下方法:
def destructively_remove_if(predicate, list):
for k in xrange(len(list)):
if predicate(list[k]):
del list[k]
break
return list
list = [
{ 'id': 1, 'name': 'John' },
{ 'id': 2, 'name': 'Karl' },
{ 'id': 3, 'name': 'Desdemona' }
]
print "Before:", list
destructively_remove_if(lambda p: p["id"] == 2, list)
print "After:", list
除非您在數據上構建類似於索引的東西,否則我認為您不會比對整個列表進行蠻力“表掃描”做得更好。 如果您的數據按您使用的鍵排序,您也許可以使用bisect模塊更快地找到您要查找的對象。
從pep448上關於解包概括(python 3.5 及更高版本)的更新,同時使用臨時變量迭代字典列表,比方說行,您可以使用 **row 獲取當前迭代的字典,合並新鍵或使用 boolean 操作從您的字典列表中過濾掉字典。
請記住 **行將 output 是一本新詞典。
例如,您的字典起始列表:
data = [{'id': 1, 'name': 'paul'},{'id': 2, 'name': 'john'}]
如果我們想過濾掉 id 2:
data = [{**row} for row in data if row['id']!=2]
如果你想過濾掉約翰:
data = [{**row} for row in data if row['name']!='John']
與問題沒有直接關系,但如果您想添加新密鑰:
data = [{**row, 'id_name':str(row['id'])+'_'+row['name']} for row in data]
它也比公認的解決方案快一點。
試試這個:例如刪除列表中的“joh”
for id,elements in enumerate(dictionary):
if elements['name']=='john':
del dictionary[id]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.