# Array of Structs: getindex, slicing, and broadcasting getfield

**URL:** <https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887>\
**Category:** General Usage\
**Tags:** question\
**Created:** [March 14, 2019, 8:50pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887 "2019-03-14T20:50:37Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 14, 2019, 8:50pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/1 "2019-03-14T20:50:37Z")

</div>

I have a struct like

```julia
mutable struct State
    ID::Int64
    kind::Symbol
    val::Float64
end

```

that contains info about one object, but what I’m interested in is `States`, a collection of multiple `State` objects. I am combining each separate `State` into an array and defining `States = Array{State}(undef, 5)`, for example. But now it’s difficult to broadcast `States.ID`. I was thinking of modifying get indices along the lines of something like

```julia
Base.getindex(states::Array{State,1}, n, ::Type{Val{:ID}}) = states[n].ID

```

but this doesn’t work.

What’s standard / best practice, and how can I ensure that updates to particular fields are reflected in the full object?

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [March 14, 2019, 9:16pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/2 "2019-03-14T21:16:07Z")

</div>

One solution is using [https://github.com/piever/StructArrays.jl](https://github.com/piever/StructArrays.jl).

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 14, 2019, 9:16pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/3 "2019-03-14T21:16:18Z")

</div>

I would suggest defining an accessor function and then broadcasting that function:

```julia
id(s::State) = s.ID

id.(states)

```

Does that work for your use case?

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 14, 2019, 9:28pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/4 "2019-03-14T21:28:50Z")

</div>

Thanks @rdeits and @mohamed82008! I’ll try out the `StructArrays` package first since from a quick `@btime` it seems that broadcasting will allocate each call to `id.(states)` whereas it does it once for `StructArrays`

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [March 15, 2019, 12:52am UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/5 "2019-03-15T00:52:51Z")

</div>

StructArrays will allocate all your `id`s contiguously, so if you’re accessing a lot of them at a time, it will probably be faster because it can be vectorized / take advantage of SIMD, while the array of structs cannot.  
Same goes for the `val`s in your State.

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 17, 2019, 4:41pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/6 "2019-03-17T16:41:44Z")

</div>

As a follow up, can I use `StructArrays` in a nested way, for example:

```julia
mutable struct State
    ID::Int64
    kind::Symbol
    val::Float64
end
mutable struct Object
    st::State
    other::Int64
end
O1 = Object(State(1, :s1, 1.0), 10)
O2 = Object(State(2, :s2, 3.0), 20)
O3 = Object(State(3, :s2, 3.0), 30)
Os = StructArray([O1; O2; O3])

```

so that I can access `Os.st.val`?

I can’t assign `StructArray(Os.st)` to `Os.st`, but I can access as `StructArray(Os.st).val`. However, this feels awkward.

EDIT: it looks like it’s possible to define `Cell` from here `https://github.com/piever/StructArrays.jl/issues/11`

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [March 17, 2019, 5:15pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/7 "2019-03-17T17:15:34Z")

</div>

IIUC, you want to access the fields in a view. Use MappedArrays:

```julia
using MappedArrays
Os_st = mappedarray((x) -> x.st, Os)

```

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 17, 2019, 5:23pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/8 "2019-03-17T17:23:33Z")

</div>

Alternatively, `Os = StructArray(objects; unwrap = t -> t <: State)` works for `objects = [O1; O2; O3]`!

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 22, 2019, 10:44pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/9 "2019-03-22T22:44:23Z")

</div>

Following up on usage of `StructArrays`, what is the best practice for updating values? For example in `Os`, I want to update a single element,

```julia
Os.st[1].ID = 100

```

but this isn’t reflected in `Os` after the assignment.

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [March 22, 2019, 11:31pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/10 "2019-03-22T23:31:30Z")

</div>

Yes, I think what you want here is `StructArrays` with the `unwrap` option. Performance-wise StructArrays works best if your struct is immutable. The trick is that you can change your “columns” even though the struct is immutable as follows:

```julia
julia> using StructArrays

julia> struct State
           ID::Int64
           kind::Symbol
           val::Float64
       end

julia> s = StructArray(State(i, :test, 1.2) for i in 1:3)
3-element StructArray{State,1,NamedTuple{(:ID, :kind, :val),Tuple{Array{Int64,1},Array{Symbol,1},Array{Float64,1}}}}:
 State(1, :test, 1.2)
 State(2, :test, 1.2)
 State(3, :test, 1.2)

julia> s.val[2] = 0;

julia> s
3-element StructArray{State,1,NamedTuple{(:ID, :kind, :val),Tuple{Array{Int64,1},Array{Symbol,1},Array{Float64,1}}}}:
 State(1, :test, 1.2)
 State(2, :test, 0.0)
 State(3, :test, 1.2)

```

That is to say doing `Os.st.ID[1] = 100` rather than `Os.st[1].ID = 100`. This way you are changing the underlying array and calling `getindex` there will return the correct thing.

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 24, 2019, 9:24pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/11 "2019-03-24T21:24:01Z")

</div>

> [@jacob-roth](#):
>
> Os = StructArray(objects; unwrap = t -\> t \<: State)

Thanks @piever! Is there a “best” way to do `Os.st.ID[1] = 100` so that `Os` changes, or if I’m doing this, is it best then to just broadcast functions like `id.()` per @rdeits above? That is, when I look at `Os[1]`, I’d like its `State` `ID` to be `100` now, too.

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [March 24, 2019, 10:32pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/12 "2019-03-24T22:32:08Z")

</div>

`os.st.ID[1] = 100` changes the underlying array, so:

```julia
julia> using StructArrays

julia> struct State
           ID::Int64
           kind::Symbol
           val::Float64
       end

julia> struct Object
           st::State
           other::Int64
       end

julia> o1 = Object(State(1, :s1, 1.0), 10);

julia> o2 = Object(State(2, :s2, 3.0), 20);

julia> o3 = Object(State(3, :s2, 3.0), 30);

julia> os = StructArrays.StructArray([o1, o2, o3]; unwrap = t -> t <: State);

julia> os.st.ID[1]
1

julia> os.st.ID[1] = 100
100

julia> os.st.ID[1]
100

julia> os[1]
Object(State(100, :s1, 1.0), 10)

```

The idea is that `os.st.ID` is the vector where all the IDs are stored (the structs themselves are not stored anyway but generated on the fly when you do say `os[3]`), so if you do `os.st.ID[3] = 10` you are changing the underlying vector and the next time the struct is generated, it’ll reflect the change.

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [March 24, 2019, 10:59pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/13 "2019-03-24T22:59:29Z")

</div>

> [@piever](#):
>
> julia\> os = StructArrays.StructArray([o1, o2, o3]; unwrap = t -\> t \<: State); julia\> os.st.ID[1] 1 julia\> os.st.ID[1] = 100 100 julia\> os.st.ID[1] 100 julia\> os[1]

Ah, I think I missed this. Thank you!!

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [October 23, 2020, 6:05pm UTC](https://discourse.julialang.org/t/array-of-structs-getindex-slicing-and-broadcasting-getfield/21887/14 "2020-10-23T18:05:46Z")

</div>

I can highly recommend StructArrays (kudos to @piever).
