0

I created a class based on a list of variables that comes from data. I made a function that will place all data inside an object of this class.

As the list of variables grows, I keep having to adjust the class and the load function. I am trying to find a way that will make my load function robust against changes in the class.

Code of the Class file:

classdef CoolClass
    properties
        prop_a
        prop_b
        prop_c
    end
end

The function I wrote to load this data into an object:

function [ object_name ] = loadCoolClass (data_file_name)

    load(data_file_name) 
    % this loads the variabels: 
    % prop_a = 1, prop_b = 2, prop_c = 3, prop_d = 4

    object_name = CoolClass;

    object_name.prop_a = prop_a;
    object_name.prop_b = prop_b;
    object_name.prop_c = prop_c;
end

Now what I would like to have is something that if I add a variable prop_d to the classdef file that it will immediately load it into the loadfile as well. something like this:

function [ object_name ] = loadCoolClass (data_file_name)

    load(data_file_name) 
    % this loads the variabels: 
    % prop_a = 1, prop_b = 2, prop_c = 3, prop_d = 4

    object_name = CoolClass
    cool_properties = properties(CoolClass)

    for i = 1:size(cool_properties,2)
        object_name.cool_property(i) = cool_property(i)
    end
end

Now I know the loop above is not valid code but I mean: check for every property if there is a variable with that name and place it in the object.

Is there a way in matlab to use a variable holding a string, as an input to load the value of the variable named after the content of the string?

Is this possible in naming content before the '=' sign Is this possible in referring to variables after the '=' sign

Suever
  • 64,497
  • 14
  • 82
  • 101

2 Answers2

3

In my experience, the easiest way to do this is to make your class a subclass of hgsetget. This gives you the ability to use set(obj, 'Key', value) and get(obj, 'Key', value) just like you would for graphics objects.

cls = CoolClass();
set(cls, 'prop_a', 1)

The second step is to specify an output to load (rather than just dumping the data into your workspace) which will give you a struct with a field for each variable in the file. This is recommended whenver loading from a file to keep your workspace tidy.

Now, the real benefit of subclassing hgsetget is that you can also pass a struct of property/values to set and get!

Example

classdef CoolClass < hgsetget
    properties
        prop_a
        prop_b
    end
end

Now if you have a file data.mat that contains values for prop_a and prop_b you can then do this.

cls = CoolClass();
data = load('data.mat')

    prop_a: 1
    prop_b: 2

set(cls, data)

If you have variables in data.mat that are not properties of your class you can easily filter these out and still use set.

toremove = setdiff(fieldnames(data), props);
set(cls, rmfield(data, toremove);

A Better Example

What I personally would do would be to write a constructor that accepts the same inputs as set (i.e. a struct or param/value pairs) and then inside of the constructor, pass them directly to set. Also, you can implement a loadobj method for the class which is essentially a static method that accepts a struct as input. You can do all of your validation within that method to remove unwanted fields.

classdef CoolClass < hgsetget
    properties
        prop_a
        prop_b
    end

    methods
        function self = CoolClass(varargin)
            set(self, varargin{:});
        end
    end

    methods (Static)
        function obj = loadobj(S)
            obj = CoolClass();

            % Remove any invalid fields
            toremove = setdiff(fieldnames(S), properties(cls));
            set(obj, rmfield(S, toremmove))
        end
    end
end

Now you could use this class two other ways than we could previously.

cls = CoolClass(load('data.mat'));
cls = CoolClass.loadobj(load('data.mat'));
Suever
  • 64,497
  • 14
  • 82
  • 101
  • 1
    Wow this is super usefull. I was working on a project with about 30 variables where we are constantly adding and removing them. your solution really helps us reduce the overhead when changing our variables. – Paco Hamers Mar 15 '16 at 14:20
  • @PacoHamers Glad to help. I have updated with a more complete example which demonstrates how you could modify the constructor to accept these structs as inputs. – Suever Mar 15 '16 at 14:33
-3

I don't know if what you are trying to do makes sense but I think you are looking for the function eval: object_name.cool_property(i) = eval(cool_property(i))

Thierry Dalon
  • 779
  • 5
  • 21
  • oh this is indeed what I look for as part of the solution. but it indeed only solves the problem from stuff after the '='. it still gives an error on object_name.cool_property(i) = ... Argument to dynamic structure reference must evaluate to a valid field name. – Paco Hamers Mar 15 '16 at 13:34
  • 3
    Noooo please don't use eval and please don't suggest using eval. – Andras Deak -- Слава Україні Mar 15 '16 at 14:01
  • your hint gave me the vital solution for the problem: the solution is: object_name.(char(cool_property(i))) = eval(cool_property(i)) – Paco Hamers Mar 15 '16 at 13:56
  • Hey @rayryeng ! you don't need to downvote my answer please. I agree eval is ugly. But this is what Paco was looking for and I think the closest answer to his question. As I've written I am not sure it makes sense at all. Maybe you have a better answer? – Thierry Dalon Mar 21 '16 at 14:39
  • 4
    @ThierryDalon Suever already has a better answer. And if there's a solution without eval, then eval should not be used. And if a user asks and you would want to answer with eval, then don't. Then if you *really* want to answer with eval, do, but *make it clear that eval is both dangerous and as inefficient as can be*. Just dropping it as a "get out of jail free card" is unhelpful, and prepare for criticism from other users. For what it's worth, I didn't downvote your answer. – Andras Deak -- Слава Україні Mar 21 '16 at 15:46