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).
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);
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
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.
Related
I am a newbie. I have problem.
I have 20 (1x100) different named vectors. I want to combine these vectors to create a 20x100 matrix with a for loop.
There are the examples of vectors.
namelist=["First","B","New"]
First = [1:100]
B = [1:2:200]
New = [4:4:400]
for i = 1: length(namelist)
new_database(i,1:end) = namelist{i}
end
But, when I want to try this I saw "The end operator must be used within an array index expression." error.
I know I can do same thing with this:
"new_database= [First;B;New]"
but i want to do this with a for loop.
Would you help me how can fix this error? or Would you explain me how can do this?
Your problem is with this line:
new_database(i,1:end) = namelist{i}
the curly braces are used with cells, exclusively and there is no need to use range indexing as you do (i, 1:end)
Generally, it is better practice to assign character arrays or strings to cells.
One question, what are you doing with the 'First', 'New' and 'B' ranges arrays?
Something like:
namelist=["First","B","New"]
First = [1:100];
B = [1:2:200];
New = [4:4:400];
new_database = cell(1, length(namelist));
for i = 1: length(namelist) % or length(new_database)
new_database{i} = namelist(i)
end
which generates this output:
EDIT: My apologies, now I see what you are trying to accomplish. You are building a database from a series of arrays, correct?
Following my previous response, you must consider some points:
1 Your new_database should be square. Regardless of the dimensions of the arrays you are passing to it, if you form a cell from them, you will invariably have empty cells if no data is passed to those rows or columns
2 In some cases, you don't need to use for-loops, where simple indexing might suffice your case problem. Consider the following example using cellstr:
titles = ["Position", "Fruits", "Mythical creatures"]
A = ["One", "Two", "Three"];
B = ["Apple", "Banana", "Durian"];
C = ["Dragon", "Cat", "Hamster"];
db = cell(4, 3);
db(1,:) = cellstr(titles)
db(2:end,1) = cellstr(A)
db(2:end,2) = cellstr(B)
db(2:end,3) = cellstr(C)
which generates this output:
I need to pass a part of a structure's name into a function.
Examples of a available structs:
systems.system1.stats.equityCurve.relative.exFee
systems.system1.stats.equityCurve.relative.inFee
systems.system2.stats.equityCurve.relative.exFee
systems.system2.stats.equityCurve.relative.inFee
systems.system1.returns.aggregated.exFee
systems.system1.returns.aggregated.inFee
systems.system2.returns.aggregated.exFee
systems.system2.returns.aggregated.inFee
... This goes on...
Within a function, I loop through the structure as follows:
function mat = test(fNames)
feeString = {'exFee', 'inFee'};
sysNames = {'system1', 'system2'};
for n = 1 : 2
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
end
end
What I like to handle in a flexible way within the loop is the middle part, i.e. the part after systems.(sysNames{n}) and before .(feeString{n}) (compare examples).
I am now looking for a way to pass the middle part as an input argument fNames into the function. The loop should than contain something like
mat{n} = systems.(sysNames{n}).(fName).(feeString{n});
How about using a helper function such as
function rec_stru = recSA(stru, field_names)
if numel(field_names) == 1
rec_stru = stru.(field_names{1});
else
rec_stru = recSA(stru.(field_names{1}), field_names(2:end));
end
This function takes the intermediate field names as a cell array.
This would turn this statement:
mat{n} = systems.(sysNames{n}).stats.equityCurve.relative.(feeString{n});
into
mat{n} = recSA(systems.(sysNames{n}), {'stats', 'equityCurve', 'relative', feeString{n}});
The first part of the cell array could then be passed as an argument to the function.
This is one of those cases where matlab is a bit unhelpful in the documentation. There is a way to use the fieldnames function in matlab to get the list of all the fields and iterate over that using dynamic fields.
systems.system1.stats.equityCurve.relative.exFee='T'
systems.system1.stats.equityCurve.relative.inFee='E'
systems.system2.stats.equityCurve.relative.exFee='S'
systems.system2.stats.equityCurve.relative.inFee='T'
systems.system1.returns.aggregated.exFee='D'
systems.system1.returns.aggregated.inFee='A'
systems.system2.returns.aggregated.exFee='T'
systems.system2.returns.aggregated.inFee='A'
dynamicvariable=fieldnames(systems.system1)
This will return a cell matrix of the field names which you can use to iterate over.
systems.system1.(dynamicvariable{1})
ans =
equityCurve: [1x1 struct]
Ideally you would have your data structure fixed in such a way that you know how many levels of depth are in your data structure.
Is there a way that I get all the structure subsubfield values of a subfield in one line ? Something like this :
struct.field(1:end).field
If I understand your question aright, you want to collect all the fields of the second-level structure, with the name 'field', into a single output array. It doesn't quite meet your request for a one-liner, but you can do it like this:
a.field1.a = 1;
a.field1.b = 2;
a.field2.a = 3;
a.field2.b = 4;
result = [];
for x = fieldnames(a)'
result = horzcat(result, a.(x{:}).a);
end
The ending value of result is [1 3]
Simple Structure Example
aStruct.subField = struct('subSubField', {1;2;3;4})
So that
aStruct.subField(1).subSubField == 1
aStruct.subField(1).subSubField == 2
Etc. Then the values of the leaf nodes can be obtained via a one-liner as
valueLeafs = [aStruct.subField.subSubField];
Which can be checked via assert(all(valueLeafs == [1,2,3,4])).
Non-Scalar Structure Example
The above one-liner also works when the leaf node values are non-scalar such that they can be horizontally concatenated. For example
bStruct.subField = struct('subSubField', {[1,2];[3,4]})
valueLeafs_b = [bStruct.subField.subSubField]; % works okay
cStruct.subField = struct('subSubField', {[1,2];[3;4]})
valueLeafs_c = [cStruct.subField.subSubField]; % error: bad arg dims
Distinct Class Structure Example
The one-line solution given previously does not work whenever the leaf node values are different class since they cannot - in general, be concatenated. However, use of arrayfun and a tricky anonymous function typically provide the required indexing technique:
dStruct.subField = struct('subSubField', {[1;2];'myString'});
valueLeafs_d = arrayfun(#(x) x.subSubField, dStruct.subField, 'UniformOutput', false)
I have a matlab structure that follows the following pattern:
S.field1.data1
...
.field1.dataN
...
.fieldM.data1
...
.fieldM.dataN
I would like to assign values to one data field (say, data3) from all fields simultaneously. That would be semantically similar to:
S.*.data3 = value
Where the wildcard "*" represents all fields (field1,...,fieldM) in the structure. Is this something that can be done without a loop in matlab?
Since field1 .. fieldM are structure arrays with identical fields, why not make a struct array for "field"? Then you can easily set all "data" members to a specific value using deal.
field(1).data1 = 1;
field(1).data2 = 2;
field(2).data1 = 3;
field(2).data2 = 4;
[field.data1] = deal(5);
disp([field.data1]);
A loop-based solution can be flexible and easily readable:
names = strtrim(cellstr( num2str((1:5)','field%d') )); %'# field1,field2,...
values = num2cell(1:5); %# any values you want
S = struct();
for i=1:numel(names)
S.(names{i}).data3 = values{i};
end
In simple cases, you could do that by converting your struct into a cell array using struct2cell(). As you have a nested structure, I don't think that will work here.
On the other side, is there any reason why your data is structured like this. Your description gives the impression that a simple MxN array or cell array would be more suitable.
My question is easily summarized as: "Why does the following not work?"
teststruct = struct('a',3,'b',5,'c',9)
fields = fieldnames(teststruct)
for i=1:numel(fields)
fields(i)
teststruct.(fields(i))
end
output:
ans = 'a'
??? Argument to dynamic structure reference must evaluate to a valid field name.
Especially since teststruct.('a') does work. And fields(i) prints out ans = 'a'.
I can't get my head around it.
You have to use curly braces ({}) to access fields, since the fieldnames function returns a cell array of strings:
for i = 1:numel(fields)
teststruct.(fields{i})
end
Using parentheses to access data in your cell array will just return another cell array, which is displayed differently from a character array:
>> fields(1) % Get the first cell of the cell array
ans =
'a' % This is how the 1-element cell array is displayed
>> fields{1} % Get the contents of the first cell of the cell array
ans =
a % This is how the single character is displayed
Since fields or fns are cell arrays, you have to index with curly brackets {} in order to access the contents of the cell, i.e. the string.
Note that instead of looping over a number, you can also loop over fields directly, making use of a neat Matlab features that lets you loop through any array. The iteration variable takes on the value of each column of the array.
teststruct = struct('a',3,'b',5,'c',9)
fields = fieldnames(teststruct)
for fn=fields'
fn
%# since fn is a 1-by-1 cell array, you still need to index into it, unfortunately
teststruct.(fn{1})
end
Your fns is a cellstr array. You need to index in to it with {} instead of () to get the single string out as char.
fns{i}
teststruct.(fns{i})
Indexing in to it with () returns a 1-long cellstr array, which isn't the same format as the char array that the ".(name)" dynamic field reference wants. The formatting, especially in the display output, can be confusing. To see the difference, try this.
name_as_char = 'a'
name_as_cellstr = {'a'}
You can use the for each toolbox from http://www.mathworks.com/matlabcentral/fileexchange/48729-for-each.
>> signal
signal =
sin: {{1x1x25 cell} {1x1x25 cell}}
cos: {{1x1x25 cell} {1x1x25 cell}}
>> each(fieldnames(signal))
ans =
CellIterator with properties:
NumberOfIterations: 2.0000e+000
Usage:
for bridge = each(fieldnames(signal))
signal.(bridge) = rand(10);
end
I like it very much. Credit of course go to Jeremy Hughes who developed the toolbox.