Extracting "one row" from a cell array of objects

As I delve further into object-oriented Octave, I’ve been replacing data structures for which I had previously been using a matrix (wherein each column is one record, and the matrix grows to the right as I add new records) with cell arrays of objects (where each object is an entire record, and the cell array has only a single row).

I’m using the word “record” here just to mean any data structure that I was previously representing as a column vector. For example, a pure sinusoidal tone can be represented by three real numbers, namely its frequency, amplitude, and initial phase; thus, when I wanted an array of sinusoids, I would use a matrix with three rows. Each sinusoid would be represented by a 3-element column vector.

In my new object-oriented approoach, each sinusoid is represented by an object with three properties, and if I want an array of them, I use a cell array of sinusoid objects.

With all of that in mind, I present to you my new function cellExtract, which extracts one “row” from a cell array of objects:

## a is a cell array of objects
## f is a string which is the name of a property or a method

## returns a vector consisting of the values of a{:}.f

function out = cellExtract(a, f)
  out = cellfun(@(o) subsref(o, struct ("type", ".", "subs", f)), a);
endfunction

So, in my example above, if tones is a cell array of objects, and each object has properties freq, ampl, and phase, then cellExtract(tones, "freq") returns an array of the freq values.

If I want to sort tones by frequency, I can go

[~, order] = sort(cellExtract(tones, "freq"));
tones = tones(order);

I haven’t yet figured out how to perform the sort in one line without resorting to a temporary variable.

I would love your feedback please on all of the above. Is it useful? Should I name it something different? Is it unnecessary because there’s a simpler way without having to write a function? Is there a way to achieve this without breaking the abstraction barrier?

Thanks!

I’ve tested this with both properties and methods. Here’s a test class:

classdef TestClass

  properties
    a = 0
    b = 0
  endproperties

  methods
    function obj = TestClass(a,b)
      obj.a = a;
      obj.b = b;
    endfunction

    function x = m(obj)
      x = obj.a + obj.b;
    endfunction
  endmethods

endclassdef

And here’s a sample interaction:

>> objs = { TestClass(1,2) TestClass(4,5) TestClass(-1,5) };
>> cellExtract(objs, "a")
ans =

   1   4  -1

>> cellExtract(objs, "m")
ans =

   3   9   4

>>

I think it might also work with a cell array of structs, but I haven’t tried that.