Anonymous reference to structures/variables?

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

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.
Because names will change each time the function to add info to and as save the struct is called. It is saving data so you will have thing Acq_1.Trial_1.Chan1, Acq_1.Trial_1.Chan2, etc and the number of acqs, trials, and channels will vary.
And these could be cut off at any point as well. I'd prefer not to spend time at start allocating a structure in memory based on theoretical maximum values.
Thus I dont see any way around dynamic naming unless there is something clever I can do that I am unaware of.
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;
But I would still have to dynamically name the structs in the struct array would I not? From what I am seeing dynamic fieldnames are okay just not dynamic variable names.
Part of the point of this so when the struct is loaded later all the data is presented in a human readable way with field names that are clear. So the struct array gives me a constant variable to work with, the array, but everything in it would still need dynamic naming.
In anycase, although there likely better approaches to what I am trying to accomplish, I am still curious whether it is possible to grab a reference to a variable that is dynamically created in the workspace via eval(). At this point it is more just for the sake of knowing.
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.
The practical application is more complicated than that. There a number of trackers and then a stream of raw data and function that processes that data to produce some logical flags.
The data needs to be organized into a clear structure. I.e.
Trial 1
Input 0
DataChunk 1
DataChunk 2
.
.
.
Data Chunk N
Raw Data: [MxN]
MetaData: Struct();
flagA:
Counter:
Input 1
Input 2
So even if I created a struct array to store trial top structs, I still need to dynamically name fields in the trial structs themselves for which input and which data chunk. Plus this obfuscates trial names and replaces them with indices in the struct array, which sort of defeats the purpose.
The best I can think to do now is just create a generic top level struct and use sprintf on the trial and input trackers etc to dynamically created fields in the top struct.
"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.
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.

Sign in to comment.

Answers (2)

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

So Dynamic Fieldnames are Okay but not Dynamic Variable names?
In my opinion, no, dynamic field names are rarely ok. You're still spending time writing code to manipulate variable names instead of spending time to write code that manipulates the data itself.
The data is already manipulated fine, I just want organize it for saving to file that is human readable. ideally would name the file experiment_0000n_ddMMYYYY_hhmmss.mat and it will open up a DataStruct with trial structures containing input structures containing data chunk structures containg raw data and a metaStruct. The names of which would be variable, i.e. trial 1, inputs 0 to 3, dataChunks 1-N, etc.
So dynamic naming seems unavoidable. Even if I premade the structure I would still need the dynamic naming to access the correct substruct and field.
"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:
"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.

Sign in to comment.

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

Products

Release

R2018b

Asked:

on 1 Aug 2019

Edited:

on 8 Aug 2019

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!