How to add another attribute in dictionary inside a one line for loop

Viewed 1620

I have a list of dictionary and a string. I want to add a selected attribute in each dictionary inside the list. I am wondering if this is possible using a one liner.

Here are my inputs:

saved_fields = "apple|cherry|banana".split('|')
fields = [
    {
        'name' : 'cherry'
    }, 
    {
        'name' : 'apple'
    }, 
    {
        'name' : 'orange'
    }
]

This is my expected output:

[
    {
        'name' : 'cherry',
        'selected' : True
    }, 
    {
        'name' : 'apple',
        'selected' : True
    }, 
    {
        'name' : 'orange',
        'selected' : False
    }
]

I tried this:

new_fields = [item [item['selected'] if item['name'] in saved_fields] for item in fields]
5 Answers

I don't necessarily think "one line way" is the best way.

s = set(saved_fields)  # set lookup is more efficient 
for d in fields:
    d['status'] = d['name'] in s

fields
# [{'name': 'cherry', 'status': True},
#  {'name': 'apple', 'status': True},
#  {'name': 'orange', 'status': False}]

Simple. Explicit. Obvious.

This updates your dictionary in-place, which is better if you have a lot of records or other keys besides "name" and "status" that you haven't told us about.


If you insist on a one-liner, this is one preserves other keys:

[{**d, 'status': d['name'] in s} for d in fields]  
# [{'name': 'cherry', 'status': True},
#  {'name': 'apple', 'status': True},
#  {'name': 'orange', 'status': False}]

This is list comprehension syntax and creates a new list of dictionaries, leaving the original untouched.

The {**d, ...} portion is necessary to preserve keys that are not otherwise modified. I didn't see any other answers doing this, so thought it was worth calling out.

The extended unpacking syntax works for python3.5+ only, for older versions, change {**d, 'status': d['name'] in s} to dict(d, **{'status': d['name'] in s}).

You could update the dictionary with the selected key

for x in fields: x.update({'selected': x['name'] in saved_fields}):

print(fields)

[{'name': 'cherry', 'selected': True}, 
{'name': 'apple', 'selected': True}, 
{'name': 'orange', 'selected': False}]
result = [
    {"name": fruit['name'],
     "selected": fruit['name'] in saved_fields } 
    for fruit in fields
]

>>> [{'name': 'cherry', 'selected': True},
 {'name': 'apple', 'selected': True},
 {'name': 'orange', 'selected': False}]

And as a one-liner:

result = [{"name": fruit['name'], "selected": fruit['name'] in saved_fields} for fruit in fields]

The proposed solutions will work even if there is more than one entry in the dicts.

Taking your inputs :

saved_fields = "apple|cherry|banana".split('|')
fields = [
    {
        'name' : 'cherry'
    }, 
    {
        'name' : 'apple'
    }, 
    {
        'name' : 'orange'
    }
]
  1. Using the Dict.update():

    >>> [item.update({'selected': item['name'] in saved_fields}) for item in fields]
    [None, None, None]
    

    Will return [None, None, None] but modifies the fields variable inplace.

    >>> fields
    [{'name': 'cherry', 'selected': True},
     {'name': 'apple', 'selected': True},
     {'name': 'orange', 'selected': False}]
    

    note that while this is a one-liner, this is not always recommended.

  2. If you want a new list without modifying fields. It can be done using ** operator on Dict cf as shown in @cs95 answer. ** explanation:

    >>> new_fields = [{**item, 'selected': item['name'] in saved_fields} for item in fields]
    >>> new_fields
    [{'name': 'cherry', 'selected': True},
     {'name': 'apple', 'selected': True},
     {'name': 'orange', 'selected': False}]
    
    >>> fields
    [{'name': 'cherry'}, {'name': 'apple'}, {'name': 'orange'}]
    
[{'name': item['name'], 'selected': item['name'] in saved_fields} for item in fields]
Related