How to update a property for all dataclass objects in a list?

Question:

I have a list of objects of the following type:

@dataclass
class Feature:
    name: str
    active: bool

and my list is:

features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]

I want to get back a list with all the features but switch their active property to True. I tried to use map() like this:

active_features=list(map(lambda f: f.active=True,features))

but it gives me an error expected parameter. How can this be achieved?

Note I thought it was following from the example, but I guess I should have clarified. I want to do this with some short of inline method, without defining a new separate function as suggested from some of the answers, but maybe it cannot be done like this?

Asked By: KZiovas

||

Answers:

Reason why your logic is not working?

It gives you error because the lambda function you’re using is trying to modify the value of f.active, which is not allowed in a lambda function. lambda functions are allowed to only for expressions that return a value, rather than statements that perform some actions.

So I think one way to do it like below-

from dataclasses import dataclass
import copy

@dataclass
class Feature:
    name: str
    active: bool


features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features = []
main_features = copy.deepcopy(features)
for f in main_features:
    f.active = True
    active_features.append(f)
print(active_features)
print(features) 

Output:

[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]

[Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]
Answered By: Always Sunny

Change the data in the old list:

from dataclasses import dataclass
from copy import deepcopy

@dataclass
class Feature:
    name: str
    active: bool

features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]

for feature in features:
    feature.active = True
print(features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]

Or make a new list of modified values:

def func(feature):
    feature.active = True
    return feature
active_features = list(map(func, deepcopy(features)))

print(active_features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]

# If you try to print the original list, You can see that the element in the original does not change.

print(features)
# [Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]

In the lambda function you can’t use the assignment operator =.

Answered By: codester_09

If you are happy modifying the instances in place, there’s no need to build a new list. Just use a regular for loop.

features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]

for f in features:
    f.active = True

If you want a list of new objects, leaving the original objects unchanged, I would still use a regular for loop:

new_features = []
for f in features:
    new_features.append(Feature(f.name, True))

though adding an instance method that can create an active feature from an existing feature would make things cleaner:

@dataclass
class Feature:
    name: str
    active: bool

    def make_new_active(self):
        return type(self)(self.name, True)

features = [...]
new_features = [f.make_new_active() for f in features]

(Though note: I am basically reimplementing dataclasses.replace, which I forgot about. See https://stackoverflow.com/a/74852093/1126841 instead.)

Answered By: chepner

You can call dataclasses’ replace which will create a copy with the changed attribute:

from dataclasses import dataclass, replace

@dataclass
class Feature:
    name: str
    active: bool
             
features = [Feature("used-to-be", False), Feature("maybe", False)]
active_features = [replace(x, active=True) for x in features]

print(active_features) # should be active
print(features) # should be intact
Answered By: farbiondriven

Just create a method on the original class.

@dataclass
class Feature:
    name: str
    active:bool
    def activate(self):
        self.active =True                                                 
        return self
                                                          
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]

active_features=list(map(lambda f: f.activate(),features))                  

active_features

[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Answered By: tomgalpin

Well if you want to do this using map you can not use lambda because lambda can only have expressions not assignments. Also it has to return something as well. Do this instead

from dataclasses import dataclass

@dataclass
class Feature:
    name: str
    active: bool
    
def activate_feature(feature: Feature):
        feature.active = True
        return feature


features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]

active_features = list(map(activate_feature, features))
print(active_features)

Output

[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]

Or if you want to go with lambda as well you can do this as well

from dataclasses import dataclass

@dataclass
class Feature:
    name: str
    active: bool
    
    def activate(self):
        self.active = True
        return self

features = [Feature("name1", False), Feature("name2", False), Feature("name3", True)]

active_features = list(map(lambda f: f.activate(), features))
print(active_features)

Output

[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Answered By: Wasi Haider
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.