Is there an easy way to get the data out of a struct by searching for his name?
I'm thinking of a struct like this:
test = struct('A', ...
[struct('Name','Adam','Data',[1 2 3]) ...
struct('Name','Eva','Data',[11 12 13])]);
Now I want to access the Data field by searching for 'Adam' or 'Eva'.
something like this:
getStructDataByName(test,'Adam')
Does someone know a script or has an idea doing this with not too much effort?
Edit:
This is my current solution:
function getDataByName(struct,fieldname)
names = getAllDataNames(struct);
thisIdx = strcmp(names,fieldname);
% or
% thisIdx = ismember(names,fieldname);
struct.A(thisIdx).Data
end
function names = getAllDataNames(struct)
for idx = 1:length(struct.A)
names(idx,:) = {struct.A(idx).Name};
end
end
Should I use strcmp() or ismember()?
Try this:
test.A(strcmp({test.A.Name}, 'Eva')).Data
Basically if you call test.A.Name it will return a comma separated list of all the names. So by putting {} around that we concatenate all those into a cell matrix. We can then use strcmp to find the indices that match the name you're after. Note that if your names can be repeated then this will return a comma separated list over all so you might want to put the curly braces around the entire expression in that case.
Related
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.
Let's say I have got a struct called data and I want to create three substructs called area, inhabitants and industry. These names are stored in a cellarray.
My method looks like this:
names={'area','inhabitants','industrie'};
for i=1:length(names)
data.(names(i)) = struct;
end
I get this error: "Argument to dynamic structure reference must evaluate to a valid field name."
However doing it like this works:
somestr = 'area';
data.(somestr) = struct;
That's why I tried:
names={'area','inhabitants','industrie'};
for i=1:length(names)
somestr = names(i);
data.(somestr) = struct;
end
But I get the same error as before.
I want to do it that way because I have to import a lot of data and want to store it in Matlab. If someone later wants to change the code it might be much easier to just change the cellarray.
Until the specific element of the cell is accessed via curly braces, the element will be a one-by-one cell and not a char. So you just need curly braces:
names={'area','inhabitants','industrie'};
for i=1:length(names)
data.(names{i}) = struct;
end
I have 5 Matlab structs. I would like to iterate over them. My current solution is the following:
all_structs = [struct1,struct2,struct3,struct4,struct5];
for single_struct = all_structs
% do stuff to each struct here
end
However, each of the structs above has a matrix with a lot of data (including some other properties). Also, whatever I change in the single_struct is not passed back to the original struct.
Question: How do I fix that? Does Matlab copy all that data again when I create the vector all_structs? Or is the data from each of the structs (struct1,...,struct5) passed by reference? Is there a better way to iterate over multiple structs?
Thanks for helping!
struct will not be passed by reference. You will need to loop over the elements in all_structs using an index and then access and modify using that index. If you need something to be treated as reference you will need to define a class for it and make the class inherit from handle. Suggested reading
for i = 1:numel(all_structs)
% do stuff to each struct here
all_structs(i).data = ones(10,5); % your code here
end
I would suggest also reading on arrayfun, though it is useful if you want to do an operation and get results. From your description it sounds like you want to modify the structs.
In case you want to modify content of original structs, without making a copy, you can use a cell array of structs names.
Then iterate the names, and use eval to modify the content.
Using eval is inefficient, so don't make it a habit...
See the following code sample:
%Create sample structs (each struct has a data element).
struct1.data = 1;
struct2.data = 2;
struct3.data = 3;
%Create a cell array containing structs names as strings.
struct_names = {'struct1', 'struct2', 'struct3'};
%Iterate all structs names
%Modify data elements of each struct using eval.
for i = 1:length(struct_names)
sname = struct_names{i}; %Get struct name from cell array.
%Evaluate a string like: 'struct1.data = struct1.data + 1;'
eval([sname, '.data = ', sname, '.data + 1;']);
end
I am trying to take the averages of a pretty large set of data, so i have created a function to do exactly that.
The data is stored in some struct1.struct2.data(:,column)
there are 4 struct1 and each of these have between 20 and 30 sub-struct2
the data that I want to average is always stored in column 7 and I want to output the average of each struct2.data(:,column) into a 2xN array/double (column 1 of this output is a reference to each sub-struct2 column 2 is the average)
The omly problem is, I can't find a way (lots and lots of reading) to point at each structure properly. I am using a string to refer to the structures, but I get error Attempt to reference field of non-structure array. So clearly it doesn't like this. Here is what I used. (excuse the inelegence)
function [avrg] = Takemean(prefix,numslits)
% place holder arrays
avs = [];
slits = [];
% iterate over the sub-struct (struct2)
for currslit=1:numslits
dataname = sprintf('%s_slit_%02d',prefix,currslit);
% slap the average and slit ID on the end
avs(end+1) = mean(prefix.dataname.data(:,7));
slits(end+1) = currslit;
end
% transpose the arrays
avs = avs';
slits = slits';
avrg = cat(2,slits,avs); % slap them together
It falls over at this line avs(end+1) = mean(prefix.dataname.data,7); because as you can see, prefix and dataname are strings. So, after hunting around I tried making these strings variables with genvarname() still no luck!
I have spent hours on what should have been 5min of coding. :'(
Edit: Oh prefix is a string e.g. 'Hs' and the structure of the structures (lol) is e.g. Hs.Hs_slit_XX.data() where XX is e.g. 01,02,...27
Edit: If I just run mean(Hs.Hs_slit_01.data(:,7)) it works fine... but then I cant iterate over all of the _slit_XX
If you simply want to iterate over the fields with the name pattern <something>_slit_<something>, you need neither the prefix string nor numslits for this. Pass the actual structure to your function, extract the desired fields and then itereate them:
function avrg = Takemean(s)
%// Extract only the "_slit_" fields
names = fieldnames(s);
names = names(~cellfun('isempty', strfind(names, '_slit_')));
%// Iterate over fields and calculate means
avrg = zeros(numel(names), 2);
for k = 1:numel(names)
avrg(k, :) = [k, mean(s.(names{k}).data(:, 7))];
end
This method uses dynamic field referencing to access fields in structs using strings.
First of all, think twice before you use string construction to access variables.
If you really really need it, here is how it can be used:
a.b=123;
s1 = 'a';
s2 = 'b';
eval([s1 '.' s2])
In your case probably something like:
Hs.Hs_slit_01.data= rand(3,7);
avs = [];
dataname = 'Hs_slit_01';
prefix = 'Hs';
eval(['avs(end+1) = mean(' prefix '.' dataname '.data(:,7))'])
How can i access the following structure path with dynamic fieldnames:
var = 'refxtree.CaseDefinition.FlowSheetObjects.MaterialStreamObjects{8}.MaterialStreamObjectParams.Pressure.Value.Text';
fields = textscan(var,'%s','Delimiter','.');
refxtree.(fields{:}) does not work because MaterialStreamObjects contains a cell array of which I want to access the 8th cell and then continue down the structure path.
In the end I want to get and set the fieldvalues.
You need to build the appropriate input to subsref, possibly using substruct. Look at the MATLAB help.
You can define an anonymous function to navigate this particular kind of structure of the form top.field1.field2.field3{item}.field4.field5.field6.field7 (as an aside: is it really necessary to have such a complicated structure?).
getField = #(top,fields,item)top.(fields{1}).(fields{2}).(fields{3}){item}.(fields{4}).(fields{5}).(fields{6}).(fields{7})
setField = #(top,fields,item,val)subsasgn(top.(fields{1}).(fields{2}).(fields{3}){item}.(fields{4}).(fields{5}).(fields{6}),struct('type','.','subs',fields{7}),val);
You use the functions by calling
fieldValue = getField(refxtree,fields,8);
setField(refxtree,fields,8,newFieldValue);
Note that fields is required to have seven elements. If you want to generalize the above, you will have to dynamically create the above functions
In this case, it is easier to just use EVAL:
str = 'refxtree.CaseDefinition.FlowSheetObjects.MaterialStreamObjects{8}.MaterialStreamObjectParams.Pressure.Value.Text';
%# get
x = eval(str)
%# set
evalc([str ' = 99']);