Get the Vector of a Vector of struct

I have a vector of a struct and want a vector of a component of the struct:

struct MyPair
  x::Float64
  y::Float64
end

vec = Vector{MyPair}()
push!(vec, MyPair(1,10))
push!(vec, MyPair(2,20))
push!(vec, MyPair(3,30))

println(vec[:].x) # Doesn't compile.
println(vec.x[:]) # Doesn't compile, either.

Does a functionality to do this already exist in Julia or in a Julia package?

I can easily write a function to extract the vector of the component but it’s a bit tedious to have to write such a function for each struct I define . . .

(Before introducing the struct, I was using a 2D array like arr[1,:] for vec.x and arr[2,:] for vec.y but I always get confused as to whether “1” represents “x” or not . . . The struct introduces an annotation, if you like.)

Are you looking for:

julia> getproperty.(vec, :x)
3-element Vector{Float64}:
 1.0
 2.0
 3.0

?

7 Likes

StructArrays.jl allows you to have an array which gives structs on iteration whilst internally storing individual fields in separate arrays.

2 Likes

You could also spit out the vector with a comprehension:

[v.x for v in vec0]

NB: renamed vec to vec0 to not collide with the Base function with the same name

3 Likes

Or, for completeness:

 map (v -> getproperty(v,:x),vec)
1 Like

Thank you all for your inputs. Each of your solutions, of course, works for my problem.

For completeness :slight_smile: I have a related question.

I also have a vector of vectors. It’s of course very similar to a 2D array, but I use a vector of vectors because both the child and parent vectors individually grow as I read data from a file. The following code omits the reading-from-file part just for simplicity:

vv = Vector{Vector{Int}}()
push!(vv, [1, 2, 3])
push!(vv, [10, 20, 30])
println(vv[:][1]) # I want the first "column" [1, 10] but you actually get [1,2,3].
a = [1 2 3; 10 20 30]
println(a[:, 1]) # we get [1,10]

So, what should we do to get the first “column” of a vector of vectors?

In my original question of this thread, we have getproperty such that

getproperty(s, :x) # is equivalent to s.x

So, I imagine there may be a getelement function such that

getelement(v, 1) # equivalent to v[1]

If there is such a function, we would be able to get the first “column” of the vector of vectors as

getelement.(vv, 1)

wouldn’t we?

Currently, I use the list comprehension [v[1] for v in vv], which is arguably easier to understand than the above. (That’s why I said “for completeness” . . . )

Broadcast getindex(), for example, to get the second element do: getindex.(vv, 2)

1 Like

Or in the special case of getting the first element from each, first.(vv)

1 Like

Thanks!!! But why getindex ? Although I saw the name somewhere, it’s never occurred to me that it actually gets an element from a vector . . .

Perhaps is it short for “get value from vv at index i” ? But if that was the intention, I would call it “get_at_index” at the very least . . .

Because you asked me, the answer is “I have no clue”

1 Like

See the discussion here:

https://github.com/JuliaLang/julia/issues/42525

I guess the direct answer to your question is that name was just chosen in the early days of Julia and no one ever considered it “bad” enough to warrant a massively breaking change to the language later on. As you see in the discussion, there is a possibility that it might be renamed in 2.0.

If it’s about discoverability (i.e. you would have never guessed that [] is syntactic sugar for getindex), you can do:

julia> x = rand(5);

julia> Meta.@lower x[1]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Base.getindex(x, 1)
└──      return %1
))))

to see what’s going on.

2 Likes

See the discussion here

Wow, thanks! That discussion already includes everything I said: getindex being counterintuitive, get element, get at index, etc.

Believe me, I searched Google for the function name for [ ] but failed to find getindex, which was the reason why I posted my question here.

you would have never guessed that is syntactic sugar for getindex

Well, before posting my question, actually I tried

([]).(vv, 1)

because I guessed the notation [ ] may be a syntactic sugar for a function call.

This is similar to how C++ and Haskell define syntactic sugars.

Meta.@lower

Reflection capability of Julia is amazing to me. Thanks for that.