Search code examples
matlabclassoopobjectclass-properties

Itererating over class properties in Matlab


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


Solution

  • 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'));