4

I have a problem with multiple conditions with list:

listionary = [{u'city': u'paris', u'id': u'1', u'name': u'paul'},
              {u'city': u'madrid', u'id': u'2', u'name': u'paul'},
              {u'city': u'berlin', u'id': u'3', u'name': u'tom'},
              {u'city': u'madrid', u'id': u'4', u'name': u'tom'}]

I try to delete items that meet both conditions simultaneously.

[elem for elem in listionary if (elem.get('name')!='paul' and elem.get('city')!='madrid')]

In this case element is removed if meet at least one condition, I try to do it in several ways, any ideas?

Expected output:

[{u'city': u'paris', u'id': u'1', u'name': u'paul'}
{u'city': u'berlin', u'id': u'3', u'name': u'tom'}
{u'city': u'madrid', u'id': u'4', u'name': u'tom'}]

I would like to remove element which meet both conditions.

mgilson
  • 300,191
  • 65
  • 633
  • 696
causeyo
  • 55
  • 4

4 Answers4

5

Try changing the and to or.

[elem for elem in listionary if (elem.get('name')!='paul' or elem.get('city')!='madrid')]

Remember de morgan's laws. Informally: when you negate a boolean expression, you have to switch and with or in addition to switching "==" with "!=".

Kevin
  • 74,910
  • 12
  • 133
  • 166
  • great, this is exactly what I was looking for. basic logical mistake, good to know. thanks a lot – causeyo Jan 15 '16 at 21:42
5

Your condition should have been like this

[e for e in data if not (e.get('name') == 'paul' and e.get('city') == 'madrid')]

Output

[{u'city': u'paris', u'id': u'1', u'name': u'paul'},
 {u'city': u'berlin', u'id': u'3', u'name': u'tom'},
 {u'city': u'madrid', u'id': u'4', u'name': u'tom'}]

This checks if the current element's name is paul and city is madrid. If both the conditions are satisfied, the not outside will flip it, so you will get False and the item will be omitted.

Basically you are checking if the current item is the one which you don't want and make the if condition fail if it is.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
2

You could use itemgetter to get all the values into a tuple and then compare that:

>>> from operator import itemgetter
>>> name_and_city = itemgetter('name', 'city')
>>> [e for e in listionary if name_and_city(e) != ('paul', 'madrid')]
Peter Wood
  • 23,859
  • 5
  • 60
  • 99
1

Your data is in fact quite static. In this case you can use namedtuple for performance on large data.

from collections import namedtuple
Profile = namedtuple('Profile', ['city', 'id', 'name'])
listionary = [Profile(*d) for d in listionary]

To improve readability, you can refactor the condition as a lambda expression (this assumes you are using namedtuple):

removed = lambda ele: \
   ele.name == 'paul' or \
   ele.city == 'madrid'
output = [ele for ele in listionary if not removed(ele)]

I think this is easier to maintain and more readable, but it may depends on who is looking at it.

Patrick the Cat
  • 2,138
  • 1
  • 16
  • 33