簡體   English   中英

從列表中刪除字典

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM