9

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]
4
  • Are you sure you copied your code correctly? That looks like it would be an error. Commented Jul 7, 2020 at 8:19
  • 1
    "I tried this" - and how did that go? What error or output did you get? How did you try to fix it? Some people (myself partially included) appreciate seeing an attempt just for the sake of it, but really the point should be to iterate upon it and break it down and debug it and maybe that leads you to asking a different question, or maybe it just leads you to the answer. Commented Jul 7, 2020 at 17:50
  • 1
    Can you do it in multiple lines? Starting there and then converting that to a single line would likely be much easier than trying to come up with a single line directly. Although what's with the "in one line" questions? Trying to force something into one line just makes for unreadable code. Commented Jul 7, 2020 at 17:55
  • A "one line for-loop" is a dict comprehension, in your case. (Or list comprehension, or set comprehension). Please read about dict comprehensions. Commented Jul 7, 2020 at 20:06

5 Answers 5

15

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}).

Sign up to request clarification or add additional context in comments.

13 Comments

You should explain to the the OP that this "one line for-loop" is a dict comprehension.
@smci It's not a dict comprehension though? It's a list comprehension which creates a dictionary each iteration. Although the focal point of my answer is that it isn't always necessary you use one.
@smci no, a dict comprehension would look like {key : value for something in something_else}. {**d, 'a': 'b', 'c': 'd'} just creates a dictionary.
cs95: No, I didn't downvote it! Not so paranoid, my friend. I don't believe I've ever downvoted any of your excellent content on SO, well maybe only one answer once. And certainly not this one.
"The second answer is just a herring to satisfy the OP's "one line" requirement, like I said not the focal point of the answer". I actually prefer it! I like it a lot. Also, people coming from functional languages will like it too.
|
5
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]

Comments

5

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}]

2 Comments

I strongly do not recommend using list comprehensions for side effects, this is a well-known antipattern. You are generating a result of [None, None, None] and throwing it away.
Good point, updated to different one liner as requested by OP.
4

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'}]
    

1 Comment

Using a list comprehension just for side effects is a Python anti-pattern
2
[{'name': item['name'], 'selected': item['name'] in saved_fields} for item in fields]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.