Creating a struct with 1D length using unitful.jl

I’m trying to build a data type to hold a measurements of a quantity. I know that this quantity will be a length, but don’t know a priori what unit that length will be. I’m having a bit of trouble coming up with syntax to guarantee that `heights` will be a length using the `Unitful` types.

``````using Unitful

struct Measurements
heights::Array{Unitful.Quantity,1} # this line
end
``````

Is there a better way to do this? Thanks for the help!

Perhaps like this?

``````julia> struct Measurements{T <: Quantity{<:Any, Unitful.𝐋}}
heights::Vector{T}
end

julia> Measurements([1u"m", 2u"cm"])
Measurements{Quantity{Rational{Int64},𝐋,Unitful.FreeUnits{(m,),𝐋,nothing}}}(Quantity{Rational{Int64},𝐋,Unitful.FreeUnits{(m,),𝐋,nothing}}[1//1 m, 1//50 m])

julia> Measurements([1u"m", 2u"s"])
ERROR: MethodError: no method matching Measurements(::Array{Quantity{Int64,D,U} where U where D,1})
``````

Edit: If you don’t want the type parameter you can use

``````julia> struct Measurements
heights::Vector{<: Quantity{<:Any, Unitful.𝐋}}
end
``````
3 Likes

thanks, sorry for the obvious question

You can also just specify a specific length unit, and on insertion convert from the given length to the length required by the vector. Is there a reason you want to keep track of what unit was originally provided?

1 Like

This may actually be a better approach – there is no need to track the original unit and it would be helpful to convert by default to a unit (say feet)

1 Like

I usually use the following when I’ve build functionality around `Unitful.jl`. There are many different aliases for common dimensions.

``````struct Measurements
heights::Vector{<:Unitful.Length}
end
``````
1 Like

Cool, if one wanted to convert to feet by default is a constructor the place to do it? Something like

``````struct Measurements
heights::Vector{<: Unitful.Length}
function Measurements(x::Vector{<: Unitful.Length})
x_conv = [Unitful.uconvert(Unitful.u"ft", xi) for xi in x]
new(x_conv)
end
end
``````

You can use `upreferred` to transform to SI units

I think this is the intended way, and it works better for performance since the vector is concretely typed:

``````julia> using Unitful

julia> a = Vector{typeof(1.0u"μm")}()
push!0-element Array{Quantity{Float64,𝐋,Unitful.FreeUnits{((μm,),𝐋,nothing}},1}

julia> push!(a,1u"m")
push1-element Array{Quantity{Float64,𝐋,Unitful.FreeUnits{(μm,),𝐋,nothing}},1}:
1.0e6 μm

julia> push!(a,7u"cm")
2-element Array{Quantity{Float64,𝐋,Unitful.FreeUnits{(μm,),𝐋,nothing}},1}:
1.0e6 μm
70000.0 μm

``````

To make your struct concrete, but also more flexible, I’d do something like this:

``````#+begin_src julia
using Unitful
struct Measurements{T <: Unitful.Length}
heights::Vector{T}
Measurements(x::Vector{<: Unitful.Length}) = [Unitful.uconvert(Unitful.u"ft", xi) for xi in x]
end

Measurements([1u"cm", 1.0u"mm", 1e10u"fm"])

#+end_src

#+RESULTS:
: 3-element Array{Quantity{Float64,𝐋,Unitful.FreeUnits{(ft,),𝐋,nothing}},1}:
:    0.03280839895013123 ft
:  0.0032808398950131233 ft
:   3.280839895013124e-5 ft

``````

If you want to change the preferred units for your use case, see https://painterqubits.github.io/Unitful.jl/stable/conversion/#Unitful.preferunits

So for example, to use feet instead of meters, you could do

``````using Unitful
Unitful.preferredunits(u"ft")

upreferred(2u"m")
# 2500//381 ft
``````