2

I would like to do something only if an object has two keys with given values:

tel = ...
nam = ...
for obj in listofobjs:
    for key, val in obj.items():
        if (key == 'tel' and val == tel) and \
           (key == 'nam' and val == name):
            # do something...

Which won't work since key and value can't be two values at the same time.

Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67

2 Answers2

3

Here's one way to do it without having to use .items():

for obj in listofobjs:
    if 'tel' in obj and 'nam' in obj and obj['tel']==tel and obj['nam']==nam:
        ...

Or you could ask for forgiveness provided all dictionary access in the if block are safe:

 for obj in listofobjs:
    try:
        if obj['tel']==tel and obj['nam']==nam:
            ...
    except KeyError:
        pass
Community
  • 1
  • 1
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
2

You don't need to loop over the .items() to do this.

for obj in listofobjs:
    if (obj.get('tel', None) == tel) and (obj.get('nam', None) == nam):

Just use .get to get the key, so that you don't get a KeyError if the key doesn't exist.

.get returns None by default, but I'm specifying it here to highlight the ability to use a different default value. If you want to use None as the default, you can leave out the second parameter from the .get call.

Replace None with a value that you know will never be a valid value for tel or nam.

Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67
  • This would fail if `tel` and `nam` are the same as the defaults returned by `.get` and the keys are not in the dictionary – Moses Koledoye Nov 04 '16 at 15:31
  • 1
    On another note `.get` already returns a default `None`, no need to specify – Moses Koledoye Nov 04 '16 at 15:33
  • You can create a sentinel object if you need to, but most of the time, `None` will work as a valid default value. – Morgan Thrapp Nov 04 '16 at 15:33
  • 1
    I'm using `None` there to highlight that the default value can be changed. – Morgan Thrapp Nov 04 '16 at 15:34
  • you don't need parenthesis around obj.get and get returns None by default so just obj.get('tel') – Alex Nov 04 '16 at 15:37
  • 2
    @Alex I highlighted in the answer why I'm keeping the `None`. – Morgan Thrapp Nov 04 '16 at 15:38
  • @MorganThrapp you still don't need parenthesis. Also, your solution is not really correct. You don't check for key being in object. So e.g if tel is None and obj is {'name': 'blah'} it will pass your condition. While author wants also to check if key in dictionary. You only check that returned value is the same as variable. Which is incorrect. – Alex Nov 04 '16 at 15:43
  • 3
    @Alex "Replace None with a value that you know will never be a valid value for tel or nam.". "You can create a sentinel object if you need to, but most of the time, None will work as a valid default value.". This is the idiomatic way of doing it. – Morgan Thrapp Nov 04 '16 at 15:44
  • @FXAMN No problem, glad I could help. – Morgan Thrapp Nov 04 '16 at 15:50