2

I have a Matlab object, that is a cell array containting structs that have almost identical structures and I want to programmatically get a (sub)field of the structs of all cell array elements.

For example, we take test

test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};

This will create a cell array with the following structure:

cell-element 1: a --> sub --> 1
cell-element 2: a --> sub --> 2
                  \-> b --> 1

It can be seen that the elements of test don't have the exact same structure, but similar. How can I get all values of the a.sub fields of the elements of the cell. I can obtain them in this specific problem with

acc=zeros(1,numel(test));
for ii=1:numel(test)
   acc(ii) = test{ii}.a.sub;
end

but I can't quite get this method to work in a more general context (ie. having different fields).

6
  • 1
    What do you mean by "having different fields"? Different type of the field values? different field names? Commented Feb 25, 2016 at 11:47
  • I mean instead of accessing the field .a.sub of each element acessing the a different field eg. .some.thing.else (which I did not include in my small example cell array) Commented Feb 25, 2016 at 11:50
  • Take a look at the getfield function: mathworks.com/help/matlab/ref/getfield.html Commented Feb 25, 2016 at 11:57
  • getfield is more or less equivalent to dynamic field names .(variable) and cannot handle field names like a.sub. Therefore I cannot use this directly without somehow splitting a.sub on the .. Commented Feb 25, 2016 at 12:05
  • 1
    okay, maybe I should put this in an answer... Commented Feb 25, 2016 at 12:25

3 Answers 3

4

You may want to use the function getfield:

%//Data to play with
test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};

%//I'm interested in these nested fields
nested_fields = {'a', 'sub'};

%//Scan the cell array to retrieve the data array
acca = cellfun(@(x) getfield(x, nested_fields{:}), test);

In case your data cannot guarantee that all the elements are the same type and size, then you need to output a cell array instead:

%//Scan the cell array to retrieve the data cell array
accc = cellfun(@(x) getfield(x, nested_fields{:}), test, 'UniformOutput', false);

Later Edit

If one wants to use different sets of nested fields for each cell element then:

%//nested_fields list should have the same size as test
nested_fields = {{'a','sub'}, {'b'}};
accm = cellfun(@(x,y) getfield(x,y{:}), test, nested_fields, 'UniformOutput', false);
Sign up to request clarification or add additional context in comments.

2 Comments

What if we want to access nested fields a.sub for the first struct, but just b for the second one? Edit: Ah maybe the nested_fields{:} automatically takes care of this for us? Nice!
@dfri Depends on how you want to express that. But okay, I'll update the answer to this case.
1

Edit: No need for recursion, as shown by @CST-link:s answer; the native getfield function can neatly unfold a cell array of fields as its second argument, e.g. getfield(foo{i}, fields{:}) instead of the call to the recursive function in my old answer below. I'll leave the recursive solution below, however, as it could have some value in the context of the question.


You can build you own recursive version of getField, taking a cell array of fields.

function value = getFieldRec(S,fields)
  if numel(fields) == 1 
    value = getfield(S, fields{1});
  else
    S = getfield(S,fields{1})
    fields{1} = [];
    fields = fields(~cellfun('isempty',fields));
    value = getFieldRec(S,fields);
  end
end

Example usage:

foo = {struct('a',struct('sub',1)), ...
  struct('a',struct('sub',2),'b',3), ...
  struct('c',struct('bar',7),'u',5)};
accessFields = {'a.sub', 'b', 'c.bar'};

values = zeros(1,numel(foo));
for i = 1:numel(foo)
  fields = strsplit(accessFields{i},'.');
  values(i) = getFieldRec(foo{i},fields);
end

With the following result

values =

     1     3     7

Comments

0

I have found a way to do this using eval:

function out = catCellStructSubField(cellStruct, fieldName)
out = zeros(1,numel(cellStruct));
for ii = 1:numel(cellStruct)
   out(ii) = eval(['cellStruct{ii}.' fieldName]);
end

Where it can be used on my test example like this:

catCellStructSubField(test, 'a.sub')

Dynamic field names (cellStruct{ii}.(fieldName)) does not work because I'm accessing sub-fields.

I know eval is often a bad idea. I'm curious for different solutions.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.