Assigning vector fields from a struct to a vector

Hi I am have trouble assigning vector fields from a struct (myStruct) to a vector (Base_Case). Please find code below. Essentially, I want to match the numerical values of the vector fields in the struct that match my “mask” name_list (which is a string vector). I wonder if anyone can help or perhaps I am trying to do the impossible?

using Parameters

@with_kw struct MyStruct # could have up to 350 fields, not all vectors 
    A :: Vector{Float64}
    B :: Vector{Float64}
    C :: Vector{Float64}
    Int_Field::Int64
    D :: Vector{Float64}
    E :: Vector{Float64}
    F :: Vector{Float64}
end

function load_data()    # This function loads data OR creates an instance (could be substitued with reading a file)
    A = zeros(1)
    B = zeros(1)
    C = zeros(1)
    D = zeros(1)
    E = zeros(1)
    F = zeros(1)

    A[1] = 1.               # Default values for test purposes
    B[1] = 2.
    C[1] = 3.
    Int_Field = 10
    D[1] = 4.
    E[1] = 5.
    F[1] = 6.
    variables = MyStruct(;A, B, C, Int_Field, D, E, F)
    return variables
end

function main()                               # Test program
    variables = load_data()                   # Load data from function load_data()
    @unpack A, B, C, Int_Field, D, E, F = variables 

    name_list =["A", "C", "F"]                # typically 3 element string vector although could be 4 or 5 (user defined)
    master_list = ["A","B","C","D","E","F"]   # this is a fixed master list string vector (same names as vector fields in MyStruct)
    Base_Case = zeros(3)                      # Float64 vector - to be found

    for n = 1:3
        for m = 1:6
            if master_list[m] == name_list[n]
                # Base_Case[n] = name_list[n]   ### How to assigned Base_Case[n] to the appropriate vector value (numercial) contained in the struct??
                println(name_list[n])
            end
        end
    end
end
main()

Use name_list = [:A, :C, :F] then you can do, for instance, getfield(variables, name_list[2]. Also the master list is fieldnames(MyStruct).

1 Like

Many thanks for quick response. I am not sure I fully understand your response. Please see below modified code as I thought you suggested, with error below. If you could modify it would be appreciated:

using Parameters

@with_kw struct MyStruct # could have up to 350 fields, not all vectors 
    A :: Vector{Float64}
    B :: Vector{Float64}
    C :: Vector{Float64}
    Int_Field::Int64
    D :: Vector{Float64}
    E :: Vector{Float64}
    F :: Vector{Float64}
end

function load_data()    # This function loads data OR creates an instance (could be substitued with reading a file)
    A = zeros(1)
    B = zeros(1)
    C = zeros(1)
    D = zeros(1)
    E = zeros(1)
    F = zeros(1)

    A[1] = 1.               # Default values for test purposes
    B[1] = 2.
    C[1] = 3.
    Int_Field = 10
    D[1] = 4.
    E[1] = 5.
    F[1] = 6.
    variables = MyStruct(;A, B, C, Int_Field, D, E, F)
    return variables
end

function main()                               # Test program
    variables = load_data()                   # Load data from function load_data()
    @unpack A, B, C, Int_Field, D, E, F = variables 

    name_list =[:A, :C, :F]                # typically 3 element string vector although could be 4 or 5 (user defined from a read in file - different every time)
    master_list = fieldnames(MyStruct)
    Base_Case = zeros(3)
    Base_Case[1] = getfield(variables, name_list[1])

end
main()
ERROR: LoadError: MethodError: Cannot `convert` an object of type Vector{Float64} to an object of type Float64
Closest candidates are:
  convert(::Type{T}, ::T) where T<:Number at number.jl:6
  convert(::Type{T}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:250
  ...
Stacktrace:
 [1] setindex!(A::Vector{Float64}, x::Vector{Float64}, i1::Int64)
   @ Base .\array.jl:843
 [2] main()
   @ Main c:\Users\peter\Documents\Julia_Code\GH_Model_Final\Test_Read.jl:39
 [3] top-level scope
   @ c:\Users\peter\Documents\Julia_Code\GH_Model_Final\Test_Read.jl:42
in expression starting at c:\Users\peter\Documents\Julia_Code\GH_Model_Final\Test_Read.jl:42

This runs:

function main()                               # Test program
    variables = load_data()                   # Load data from function load_data()

    name_list =[:A, :C, :F, :No]                # typically 3 element string vector although could be 4 or 5 (user defined from a read in file - different every time)
    master_list = fieldnames(MyStruct)
    base_case = Vector{Any}(undef, 3)
    for (i,n) in enumerate(name_list)
        if n in master_list
            base_case[i] = getfield(variables, n)
        end
    end
    return base_case
end

although whether it is what you want, I don’t know.

Thanks. Your solution is almost what I want although I need some clarification on name_list if possible. In your example you have explicitly defined the contents of name_list, with the : before each element and with :No at the end of the vector(last element). I am not sure what the : and :No does? Also, in my actual program, I will be reading name_list from a text file as follows (see below), rather than being able to define it explicitly in the program. How do I make my read-in name_list compatible with your program? A typical data file (Name_List.txt) might be:
A
C
F

    name_list = String[]                # initialize empty string array

    open("Name_List.txt", "r") do f
        for ln in eachline(f)
            push!(name_list,ln)
        end
    end

Thanks again for taking on this challenge. You have certainly increased my understanding of some interesting Julia commands

Good to hear that we’re making progress. Regarding your question, this would be my REPL session to find out:

julia> typeof(:A)
Symbol

julia> typeof(:No)
Symbol

help?> Symbol
search: Symbol

  Symbol

  The type of object used to represent identifiers in parsed julia code (ASTs). Also often used as a name or label to identify an entity (e.g. as a dictionary key). Symbols can be entered
  using the : quote operator:

  julia> :name
  :name
  
  julia> typeof(:name)
  Symbol
  
  julia> x = 42
  42
  
  julia> eval(:x)
  42

  Symbols can also be constructed from strings or other values by calling the constructor Symbol(x...).

  Symbols are immutable and should be compared using ===. The implementation re-uses the same object for all Symbols with the same name, so comparison tends to be efficient (it can just
  compare pointers).

  Unlike strings, Symbols are "atomic" or "scalar" entities that do not support iteration over characters.

  ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  Symbol(x...) -> Symbol

  Create a Symbol by concatenating the string representations of the arguments together.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> Symbol("my", "name")
  :myname
  
  julia> Symbol("day", 4)
  :day4

Note how you can do Symbol("A") and that :No is just another symbol not some “closing bracket” as you thought.

Thanks for that. I must remember to use the REPL help more often. I am a self taught FORTRAN programmer still coming to terms with many of Julia’s intricacies’. Could you shed some light on how to read name_list in from a file. I tried putting a colon in front of each of the vector fields in the text file but to no avail(see file below at end of program). It seems my name_list input vector does not contains Symbols (rather it is just a string array, albeit with colons in front of each element), making the iterative comparison impossible. Is there a way to make my name_list vector the same “type” as yours and allow the iterative comparison to be made? Perhaps this is a dumb question and a new approach is required. Thanks again for your efforts. Feel free to bail out if this is taking too much of your time.

function main()                           # Test program
    variables = load_data()               # Load data from function load_data()
    @unpack A, B, C, Int_Field, D, E, F = variables 

    #name_list =[:A, :C, :F]         # typically 3 element string vector
    #@show name_list
    name_list = []                # initialize empty  array

    open("Name_List.txt", "r") do f
        for ln in eachline(f)
            push!(name_list,ln)
        end
    end
    @show name_list
    master_list = fieldnames(MyStruct)
    base_case = Vector{Any}(undef, 3)

     for (i,n) in enumerate(name_list)
         if n in master_list
             base_case[i] = getfield(variables, n)
         end
     end
     @show base_case

end
main()
name_list = Any[":A", ":Ma", ":B", ":F"]
base_case = Any[#undef, #undef, #undef]
3-element Vector{Any}:
 #undef
 #undef
 #undef

Assuming that you have one name per line, then just

push!(name_list, Symbol(ln) )

(within the file, don’t have a :). Note that ?Symbol does tell you how to construct a symbol from a string.

1 Like

Yeh that works! Without your help I would not have found this solution. My problem is “you don’t know what you don’t know” and this is particularly the case when basic tutorials do not touch on such subjects. Best regards and thanks again. Peter

1 Like