Array of Structs: getindex, slicing, and broadcasting getfield

question

#1

I have a struct like

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

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

but this doesn’t work.

What’s standard / best practice?


#2

One solution is using https://github.com/piever/StructArrays.jl.


#3

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

id(s::State) = s.ID

id.(states)

Does that work for your use case?


#4

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


#5

StructArrays will allocate all your ids 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 vals in your State.


#6

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

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


#7

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

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

#8

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