Array of properties of an array of objects

43 visualizaciones (últimos 30 días)
Daniel Rudrich
Daniel Rudrich el 4 de Feb. de 2020
Editada: Captain Karnage el 27 de Jun. de 2023
Hi all,
assume having a class called 'Coordinate' with three properties 'x', 'y', and 'z'.
Now when creating an array of objects and accessing one property of that array, like:
a = Coordinate(someDefaultValues);
b = [a a];
b.x
it results in:
ans =
1
ans =
1
Is there a way, by changing the class itself, to get an array of 'x'-Properties instead of a comma-separated list?
(I don't want to use [b.x], which would be a work-around.)
When I set a breakpoint at
function x = get.x (obj)
% do something
end
I observe, that this method is called n times with n beingt the number of elements in the array of Coordinate objects.
However, I can't find any method which iterates through all elements and calls this get-method. Even dbstack doesn't show any method above that one.
Is there any? Or is there actually no way to do this?
  1 comentario
Adam
Adam el 4 de Feb. de 2020
I've always used the [b.x] syntax, but I'm not aware that it's possible to achieve it via the get function. Obviously writing your own regular class function you could do it, but it would be extra messy work far less neat than just using square brackets, e.g.
function res = getProperties( obj, varargin )
...
end
You will get obj as the full array of objects if you pass them in so you can program what you like then to handle that, e.g. passing in the name of the property you want as an argument and putting the results into an array yourself by using the [obj.x] syntax internally.
Alternatively
doc matlab.mixin.SetGet
can be used to return multiple properties. It's not something I've ever used, so not sure if you can get one property from multiple objects or just multiple properties from one object. It allows behaviour like you get with graphics objects using the old-style get(...) and set(...) syntax where you can set multiple properties together.

Iniciar sesión para comentar.

Respuestas (3)

Guillaume
Guillaume el 4 de Feb. de 2020
"Is there a way, by changing the class itself, to get an array of 'x'-Properties instead of a comma-separated list?"
Not really. This is fundamentally how classes work in matlab. Note that structures work exactly the same in this respect. The only thing you could do is create a class method that returns the array. As you've established you can't do that within the property getter.
classdef someclass
properties
x
end
methods
function valarray = getx(this) %note that this is not the get.x property getter
%this is a proper class method called with eg. result = b.getx;
valarray = [this.x];
end
end
end
but as you can see it just encapsulate the [b.x] in a method, so you don't gain anything.
"I don't want to use [b.x], which would be a work-around"
It is not a workaround. It is exactly how you're supposed to do it.
The other option is to redesign your class so you have only one instance that store x, y, z as an array. Depending on what you're doing this may or may not be appropriate.
  2 comentarios
Daniel Rudrich
Daniel Rudrich el 4 de Feb. de 2020
Thanks for the answer!
I also thought of something like this, maybe even set the property name to xInternal and write a method called x which then returns [this.xInternal]. However, I might have to figure out how to handle something like
a.x = 3;
I guess, that's simply not possible.
"The other option is to redesign your class so you have only one instance that store x, y, z as an array. Depending on what you're doing this may or may not be appropriate."
This seems to be even the best option, with overriding vertcat, horzcat, cat, ..., I can simply prevent the creation of object arrays, but turn it into a Coordinate with properties being arrays.
Captain Karnage
Captain Karnage el 26 de Jun. de 2023
I would still argue that [b.x] is a workaround. It's fine if you are using only horizontal arrays - but what if you have a vertical array of your object? What if you have a 2-D matrix of your object? [b.x] will not work in either of these cases.

Iniciar sesión para comentar.


Captain Karnage
Captain Karnage el 26 de Jun. de 2023
Here's one possibility. Add this function (that I'm just calling get, but name it whatever you want) as a method in your class. It's not as nice as just being able to use dot notation, but it allows you to pass a variable name to your class and get a matrix of that property the same shape (and correct locations) of the original object, no matter the size and type of array you have.
function outprop = get( obj, propname )
arguments
obj
propname { mustBeText } = '';
end
%Note: if you just want it to error out on a bad property name, you can
%remove the if / else
if isprop( obj, propname )
%Get the size of your object array
sizeIn = size(obj);
%Convert the CSL otuput of the dot notation to a horizontal array
outprop = [ obj.(propname) ];
%Reshape the horizonal array to an array the same size as the
%original object
outprop = reshape(outprop, sizeIn);
else
outprop = [];
end
end
Now you can do b.get('x') to get all the x properties of your b object array.

Captain Karnage
Captain Karnage el 27 de Jun. de 2023
Editada: Captain Karnage el 27 de Jun. de 2023
I was feeling extra crazy today, so I decided to set out to actually answer the OP's original question as stated.
"Is there a way, by changing the class itself, to get an array of 'x'-Properties instead of a comma-separated list?"
YES, actually, there are two ways to get an array of 'x'-Properties instead of a comma seaparated list by changing the class itself!
  1. You can do it by overloading the subsref function Code Patterns for subsref and subsasgn Methods
  2. You can do it by making it a subclass of matlab.mixin.indexing.RedefinesDot (this is the new, recommended method for R2021b and later)
I'm not so crazy as to do them both today, so did the simplest form of #1, above, using subsref as I have done subsref overloading before. I have not yet used the new RedefinesDot superclass, but if you want to try that, you can use the above link for reference.
I've posted my code below. You will need to add this subsref function as a method in your class for it to work. As an overview, here's what it does:
  1. Gets size of the input object array
  2. Checks that there is only one (or less?) output argument (nargout <= 1) and only one "layer" of subscripts.
  3. Creates a temporary cell array the same size as the output array
  4. Calls the built-in function, simulating as many output arguments as there are elements in the array and placing the output in the temporary cell array
  5. Removes the cell wrapper and horizonally concatenates the output of the temporary array
  6. If the number of elements in the horizontal array match the number of elements in the object array, reshapes them to match the object array
  7. Puts a single cell wrapper around the reshaped array and outputs as varargout
As I stated, this example is the SIMPLEST method to get the job done as stated. This will only have the desired output for SCALAR properties of your class. If any properties are arrays of any sort, it will go back to the default output of the function. It would be possible to alter the function to accomodate some of these others cases using cell arrays, so feel free to expand upon this if that's what you want.
function varargout = subsref( obj, params )
osize = size(obj);
numobj = prod(osize); %numel(obj) would also work
%Treat char array as a single value
if ischar(params.subs)
numSubs = 1;
else
numSubs = numel(params.subs);
end
%Only want special logic if there's only one output argument
%Also restricting to only 1 subscript, for now - can expand
%code to handle multiple subscripts
%NOTE: if typed in the command window, then nargout = numelobj
%by definition - will still print as CSL in command window
if ( ( nargout <= 1 ) && ( numSubs == 1 ) )
switch params.type
case '.'
%Create Temporary array same size as object
%array
tempargout = cell(osize);
%Simulate there being as many arguments as
%there are objects
[ tempargout{1:numobj} ] = builtin( 'subsref', obj, params);
%Now convert output to horizontal array
arrayout = [ tempargout{:} ];
if ( numel(arrayout) == numobj )
%If the number of elements match,
%reshape the horizonal array to the
%same dimensions as the object
arrayout = reshape(arrayout, osize);
varargout = { arrayout };
else
%If the number of values do not match, then one ore more properties are arrays
%Therefore, call the built-in function
%so as not to error out
[ varargout{1:nargout} ] = builtin( 'subsref', obj, params );
end
otherwise
%Otherwise, just use the builtin function
%NOTE: this line must be here, or else
%function will error out
[ varargout{1:nargout} ] = builtin( 'subsref', obj, params );
end
else
%If multiple out arguments, use the standard method to
%populate
%NOTE: this line must be here, or else
%function will error out for multiple subscripts
[ varargout{1:nargout} ] = builtin( 'subsref', obj, params );
end
end

Categorías

Más información sobre Logical en Help Center y File Exchange.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by