2

I have a list of dictionaries in the following format:

list = [
{'name':'bob','age':12},
{'name':'jill','age':34}
]

I want to remove the first dictionary where the name equals a value. Is there a better way of doing this?

value = 'bob'
for dict in list:
    if dict['name'] == value:
        list.remove(dict)
        break

Output:

list = [
{'name':'jill','age':34}
]
lol
  • 481
  • 6
  • 18
  • Your example only deletes the `name` key, not the entire dictionary. – MrAlexBailey Jan 13 '17 at 15:58
  • 1
    Possible duplicate of [How to remove a key from a python dictionary?](http://stackoverflow.com/questions/11277432/how-to-remove-a-key-from-a-python-dictionary) – Mohammad Yusuf Jan 13 '17 at 15:59
  • 2
    It's a bad practice to overwrite default `list` – Kh40tiK Jan 13 '17 at 15:59
  • You might want to try `list.remove(dict)`. (But was @Kh40tiK pointed out `list` and `dict` are "reserved" names, so you better choose different ones.) – a_guest Jan 13 '17 at 16:02
  • @lol: can you turn the dict into something like dict(bob=12, jill=34)? This way you never have duplicate names in the first place – hansaplast Jan 13 '17 at 16:06
  • Isn't this question not so much a duplicate of the question @MYGz linked above than rather of [List filtering: list comprehension vs. lambda + filter](http://stackoverflow.com/questions/3013449/list-filtering-list-comprehension-vs-lambda-filter)? That question explicitly addresses the pythonicity of different ways of doing what the OP wants to do. – Schmuddi Jan 13 '17 at 16:25
  • I'm sorry everyone I messed up in my example code, it should of been `list.remove(dict)` – lol Jan 13 '17 at 21:03

7 Answers7

3
list1 = [
{'name':'bob','age':12},
{'name':'jill','age':34}
]

value = 'bob'

for i, item in enumerate(list1):
  if item['name'] == value:
    list1.pop(i)
    break
gipsy
  • 3,859
  • 1
  • 13
  • 21
1

This is a perfect pandas problem. pandas is part of the PyData suite (I think) and it's widely accepted. I would use it for this type of problem fosho.

Python 3.5.2 |Anaconda custom (x86_64)| (default, Jul  2 2016, 17:52:12)
[GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas as pd
>>> query_list = [ {'name':'bob','age':12}, {'name':'jill','age':34} ]
>>> DF = pd.DataFrame(query_list)
>>> DF
     age  name
0   12   bob
1   34  jill
>>> mask = DF["name"] != "bob"
>>> mask
0    False
1     True
Name: name, dtype: bool
>>> DF2 = DF.loc[mask,:]
>>> DF2
     age  name
1   34  jill
>>> DF2.to_dict()
{'age': {1: 34}, 'name': {1: 'jill'}}
O.rka
  • 29,847
  • 68
  • 194
  • 309
  • 7
    I'd say using `pandas` for such a simple task is an overkill. – a_guest Jan 13 '17 at 16:05
  • 1
    Depends on how big the dictionary is though. If this is a toy example of a text file that's 500 MB then it would be better to do it this way. I try to avoid `for-loops` whenever I can after watching Jake Vanderplas's video https://www.youtube.com/watch?v=EEUXKG97YRw – O.rka Jan 13 '17 at 16:07
  • 1
    Well you got a point here. When data sets are large I agree that you might want to work with professional tools (where `pandas` comes in just right). Because the question doesn't reveal the dimension of the problem a simpler solution might be more appropriate. But in any case your answer has an educational value, so I row back on my previous statement. Using `pandas` for this task one learns about a nice tool on the way, overkill or not! – a_guest Jan 13 '17 at 16:16
  • 1
    But the OP explicitly asks about the pythonic way. I love ``pandas`` and all, but there are some things about it that are, well, not exactly pythonic. The construction ``DF.loc[mask,:]`` for example doesn't even look like Python if taken out of context. – Schmuddi Jan 13 '17 at 16:33
  • This is true, it does ask for the most pythonic which I think is @a_guest 's answer with `filter`. When I think of `pythonic` I think of `python` and the main components of the [pydata ecosystem](http://image.slidesharecdn.com/1idanielrodriguez-160614230356/95/connecting-python-to-the-spark-ecosystem-3-638.jpg?cb=1465945555). The new macbook pros ship with `NumPy` already installed which starts to blur the lines of `pythonic`. – O.rka Jan 13 '17 at 19:24
  • My data will be less that 200 lines. – lol Jan 13 '17 at 21:04
1

If you want a one liner, you could use a generator expression with next(), which short-circuits once you've found the first item to remove, and then use list.remove():

l.remove(next(d for d in l if d['name'] == value))

Example:

>>> l = [{'name':'bob','age':12},{'name':'jill','age':34}]
>>> value = 'bob'
>>> l.remove(next(d for d in l if d['name'] == value))
>>> l
[{'name': 'jill', 'age': 34}]

Note this will raise a StopIteration is the value is not found, which can be avoided, but it's a bit longer because although next() has a default argument, list.remove() does not:

>>> l = [{'name':'bob','age':12},{'name':'jill','age':34}]
>>> value = 'bob'
>>> value_to_remove = next((d for d in l if d['name'] == value), None)
>>> 'Value not in list' if value_to_remove is None else l.remove(value_to_remove)
>>> l
[{'name': 'jill', 'age': 34}]
>>> value_to_remove = next((d for d in l if d['name'] == value), None)
>>> 'Value not in list' if value_to_remove is None else l.remove(value_to_remove)
'Value not in list'
Chris_Rands
  • 38,994
  • 14
  • 83
  • 119
  • I'm confused as to why you didn't do: `value_to_remove = next((d for d in l if d['name'] == value), None)`, and then `l.remove(value_to_remove) if value_to_remove != None` – lol Jan 13 '17 at 21:12
0

From python's documentation on dictionaries.

https://docs.python.org/3/library/stdtypes.html#dict

d.clear() will clear all the values of a dictionary.

value = 'bob'
for dict in list:
    if dict['name'] == value:
        #Empty dictionary of all values, will now return {}
        dict.clear()
        break

If the dictionary is in a list of others, you can simply pop it out of the list or delete it like this.

list2 = [x for x in list1 if x != {}]
Westly White
  • 543
  • 9
  • 14
0

In my opinion using filter is the most pythonic way:

new_list = filter(lambda x: x['name'] != value, original_list)

However this doesn't remove from the original list and creates a new one in memory. Assigning old_list = filter(...) mimics removing from the original one (in the local scope at least) however there is still the memory overhead. For small lists (and most cases) this won't play a role. If it does then the following might be more appropriate:

for item in original_list:
    if item['name'] == value:
        original_list.remove(item)
        break
a_guest
  • 34,165
  • 12
  • 64
  • 118
  • " Assigning `old_list = filter(...)` " wil NOT have the same effect as modifying the list in place if there's any other reference to the list. Not to say it's a good idea to modify in place a list which is shared but that's another question ;) – bruno desthuilliers Jan 13 '17 at 16:44
0

I would create a separate utility function to do it. The following first determines which (if any) dictionaries meet the criteria, and then pops (deletes) the first one found.

from operator import itemgetter

people = [
    {'name': 'bob', 'age': 12},
    {'name': 'jill', 'age': 34},
    {'name': 'bob', 'age': 14},
]

def remove(name, seq):
    """Remove first dictionary in seq with 'name' entry equal to name."""
    matches = [i for i,n in enumerate(map(itemgetter('name'), seq)) if n == name]
    if matches:
        seq.pop(matches[0])


remove('bob', people)
print(people)  # -> [{'name': 'jill', 'age': 34}, {'name': 'bob', 'age': 14}]
martineau
  • 119,623
  • 25
  • 170
  • 301
0
list1 = [
     {'name': 'bob', 'age':12},
     {'name': 'jill', 'age':34}
]

map(lambda d: d.pop('name', 0) if d['name'] == 'bob' else d, list1)
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
Liron Lavi
  • 421
  • 1
  • 5
  • 16