Anonymous reference to structures/variables?
Show older comments
Is there a way to reference a struct or a variable without explicitly using the name?
For example if I want create structs whose name can vary via somehting like
eval(sprintf('Struct_%d = struct();', counterVar));
This will create a bunch of structs like Struct_1, Struct_2, .... etc
Now I want to edit the fields of struct 1, I can't just say Struct_1.dataField = 123; because that is hard coded to be Struct_1 and this might be Struct_23...
What I could do is continue to abuse eval and sprintf and just do eval(sprintf('Struct_%d.dataField = %d', counterVar, myData)); but I am curios whether I can just get some sort of generic handle or memory reference to that struct I just created so that I could just do h.dataField = myData; and have it correctly update the right struct.
10 Comments
Adam Danz
on 1 Aug 2019
One of the reason's your running into this difficulty is because of your dynamically named variables (ie, use of eval) in the first place. This is why it is highly recommended not to use dynamic variables.
Instead, why not use a structure array? It sounds like your data are perfect for structure arrays.
Jacob Franklin
on 2 Aug 2019
The clever thing to do is avoid dynamic variable naming at all cost. You certainly won't make it better by adding another layer of dynamic field naming.
Take this example (you can imagine it in a loop).
% "This will create a bunch of structs like Struct_1, Struct_2, .... etc"
s(1) = struct();
s(2) = struct();
s(3) = struct();
% "Now I want to edit the fields of struct 1, I can't just say
% Struct_1.dataField = 123; because that is hard coded to be
% Struct_1 and this might be Struct_23...
s(1).dataField = 123;
s(2).dataField = 999;
Jacob Franklin
on 2 Aug 2019
Guillaume
on 2 Aug 2019
I am still curious whether it is possible to grab a reference to a variable that is dynamically created in the workspace via eval().
No, once you've started with eval, it's eval all the way.
"But I would still have to dynamically name the structs in the struct array would I not?"
Not necessarily. I didn't dynamically name 's' in my comment above. That's something you just name once. If you want to add more similar structures to the array s(end+1) = MyNewStruct.
" From what I am seeing dynamic fieldnames are okay just not dynamic variable names"
Debatable.
Jacob Franklin
on 2 Aug 2019
"this obfuscates trial names and replaces them with indices in the struct array, which sort of defeats the purpose."
I would make the opposite claim. This eliminates the need for trial names and replaces that with indexing. To access trial 12 (for example) under your arrangement, you'll also need to dynamically produce "Trial12" (as Guillaume said, once you go down the dynamic variable rabbit hole there's no coming back). Instead, you could just do Trial(12) to access all of its fields and subfields. Play around with this demo below to get my point. If you want trial 12, input 2; Trial(12).input(2).
trial(1) = struct();
trial(2) = struct();
trial(3) = struct();
trial(1).input(1).DataChunk1 = rand(6);
trial(1).input(1).DataChunk2 = rand(3);
trial(1).input(2).DataChunk1 = rand(6);
trial(1).input(2).DataChunk2 = rand(3);
trial(2).input(1).DataChunk1 = rand(6);
trial(2).input(1).DataChunk2 = rand(3);
alternatively
trial(1).input(1).('DataChunk1') = rand(6);
trial(1).input(1).('DataChunk2') = rand(3);
alternatively
trial(1).input(1).DataChunk{1}= rand(6);
trial(1).input(1).DataChunk{2} = rand(3);
*Note that you'll have to start with input 1 (not input 0) but that should be simple to manage.
Having said all this, I do realize that the way the data are generaged may not be as streamlined as my example but this is something to consider. Even if you have to revamp the way your data are generated, it may still be worth the time.
Guillaume
on 3 Aug 2019
It may be that using a much flatter format such as a table would be a lot more practical. I.e. a table with variables: trial_number, input_number, datachunk_index, rawdata, ...
This would be much easier to filter data according to any of the property, e.g. all everything to do with 1st input:
filtered = thetable(table.input_number == 1, :)
this is much harder to do with a multilevel struct. With a table, you also get access to lots of grouping functions such as groupsummary.
"...this obfuscates trial names and replaces them with indices in the struct array, which sort of defeats the purpose"
In fact it lets you achieve a much more useful goal: separation of code and data.
Meta-data (e.g. test parameters, subject names, indices, etc.) are data, and data do NOT belong in variable names or fieldnames. Mixing (meta-)data into code (as fieldnames or variable names) makes accessing that (meta-)data slow, complex, and hard to debug.
Indexing is much more efficient, clearer, less buggy, and easier to debug. A flatter structure using indexing would be much easier to work with.
Answers (2)
Steven Lord
on 2 Aug 2019
Edited: Steven Lord
on 2 Aug 2019
Don't define variables with dynamic names. One alternate approach would be to create fields in a struct array dynamically. The name of the struct array wouldn't change, just the names of the fields. Let's create 5 random names consisting of the word 'mydata' followed by a number between 1 and 100.
x = randperm(100, 5);
names = "mydata" + x
Make a struct using those names.
for whichfield = 1:numel(names)
mystruct.(names(whichfield)) = whichfield;
end
mystruct
If you're using a release that predates the string class you'll need to build the names array differently, perhaps using sprintf or num2str inside the loop.
for whichfield = 1:numel(x)
thename = sprintf('mydata%d', x(whichfield))
mystruct2.(thename) = whichfield;
end
mystruct2
Let's see if mystruct has a field named mydata42, or just retrieve it if it does (and tell the user it doesn't if it doesn't.)
is42TheAnswer = isfield(mystruct, 'mydata42')
thedata = NaN;
try
thedata = mystruct.('mydata42');
catch ME
warning(['The mystruct struct does not have a field named ', ...
'mydata42. Returning NaN.'])
end
thedata
4 Comments
Jacob Franklin
on 2 Aug 2019
Jacob Franklin
on 2 Aug 2019
"I just want organize it for saving to file that is human readable"
Keeping meta-data and code separate is best in terms of code simplicity, efficiency, and ease of debugging. What you are trying to do makes your code more complex, liable to bugs, and less efficient, which has a direct negative effect on your productivity:
Making data "human readable" would be achieved using a simpler structure that makes it easier to process that data:
https://www.mathworks.com/help/matlab/matlab_prog/access-multiple-elements-of-a-nonscalar-struct-array.html
"So dynamic naming seems unavoidable."
I very much doubt that:
- If you are exporting the data to a .mat file then you can use the '-struct' option.
- No other file types store variable names, so the variable names are irrelevant.
"Even if I premade the structure I would still need the dynamic naming to access the correct substruct and field."
Yes. Better than dynamic variable names, although it still mixes up meta-data and code, forcing you to write slow and complex code. Indexing is much simpler.
Walter Roberson
on 2 Aug 2019
0 votes
Use dynamic field names on a fixed variable name. Then save() with the -struct option. That will make the top level fields of the struct into separate variables in the mat file.
Categories
Find more on Structures in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!