For loop on the elements of a mutable struct

Hi ! :smiley:
I have a question about mutable structs. I have a model model composed of several compartments, each characterized by (say) 2 values a and b. I would like to make a for loop to test different values of a and b for these buckets. The following code does not work because "type Model has no field compartment". Is this normal behavior, and if so 1) why? and 2) how to do otherwise?

Thanks ! :smiley:

mutable struct Compartment
    a::Int64
    b::Float64
end

Base.@kwdef mutable struct Model
    compartment1::Compartment
    compartment2::Compartment

    compartment_list::Vector{Compartment} = [compartment1,  compartment2]
end

c1 = Compartment(2, 16.0)
c2 = Compartment(5, 67.0)

model = Model(compartment1 = c1, compartment2 = c2)

for compartment in model.compartment_list
    model.compartment.a +=1
    model.compartment.b += 6.3
end

your Model has three fields: compartment1, compartment2, compartment_list

it does not have a field called compartment

1 Like

To elaborate on this, what you likely want to do is the following:


for compartment in model.compartment_list
    compartment.a +=1
    compartment.b += 6.3
end

note that in your loop you are already iterating over the compartments of your model

2 Likes

But the for loop is not able to treat compartment as a β€œvariable” and replace it with compartment1 and compartment2?

inside your loop, the compartment is of type Compartment, which has two fields, a and b.

But look at this line

model.compartment.a +=1

the model here is the Model() you just defined outside of for loop, which does not have a field called compartment

what you might wanted to do is this:

for compartment in model.compartment_list
    compartment.a +=1
    compartment.b += 6.3
end
1 Like

Oh great thank you very much! I thought that by doing like this, I will only modify the values of c1 and c2, but not the values contained in model.

c1 and c2 are the same things that are contained by model.

btw, in this case you actually don’t need Model itself being mutable, you should figure out why

2 Likes

As a side note, you could iterate over the fields of Model without having an explicit list of its compartments:

julia> mutable struct Compartment
           a::Int64
           b::Float64
       end

julia> Base.@kwdef mutable struct Model
           compartment1::Compartment
           compartment2::Compartment
       end
Model

julia> c1 = Compartment(2, 16.0);

julia> c2 = Compartment(5, 67.0);

julia> model = Model(compartment1 = c1, compartment2 = c2)
Model(Compartment(2, 16.0), Compartment(5, 67.0))

julia> for compartment in fieldnames(Model)
           c = getfield(model,compartment)
           c.a += 1
           c.b += 6.3
       end

julia> c1
Compartment(3, 22.3)

julia> c2
Compartment(6, 73.3)

2 Likes

In addition, you can make Model iterable:

julia> Base.iterate(m::Model, n=1) = if n ≀ fieldcount(Model); getfield(m, n), n+1 end

julia> for comp in model
           comp.a += 1
           comp.b += 6.3
       end

julia> c1
Compartment(3, 22.3)

julia> c2
Compartment(6, 73.3)
1 Like