0

I want to get the content of a variable from its name as a string:

 %suppose that I have structures variables  in the workspace:
    struct_1=struct('field1' ,"str",'field2',0:5)
    struct_2=struct('field1' ,"str",'field2',0:10)
    struct_3=struct('field1' ,"str",'field2',0:20)
    %and I have an other  variable like:
    a=5
    var2='hello'
    %and I want to concatenate all these structures in the same structure
    %So i wan to get their name.
    %I don't know if there is an other way to get just the structure variables
    structures=who(str*)
    array=[]
    for i=1:length(structures)
        array=[array; structures(i). field2]; % here I get an error because the content of structures are string representing the variable name
    end
%create the new struct
  str='newstr_value'
  struct_4=struct('field1',str, 'field2',array)

How to fix this error and is there any way to do this better ?

Sardar Usama
  • 19,536
  • 9
  • 36
  • 58
LearnToGrow
  • 1,656
  • 6
  • 30
  • 53
  • What are you trying to achieve? If you know the string of a variable and want to call/define variables dynamically, you can use `eval`. But you should only ever use it in matlab when you absolutely have to. Even then, it's probably still better that you don't. – Argyll Mar 28 '18 at 15:50
  • How are you generating these structs? Why were they not created as an array of structs to begin with? Where are the variables `a` and `var2` used? – beaker Mar 28 '18 at 15:53
  • @Argyll, I recuperate the name of structures as string with who(str*). from string I want to access the content of structures(i).field2. That is it – LearnToGrow Mar 28 '18 at 15:55
  • @beaker, Suppose that these structures are generated from a toolbox and I don't have control – LearnToGrow Mar 28 '18 at 15:56
  • You are saying you want to "iterate" through a collection of variables with naming convention `struct_#`? – Argyll Mar 28 '18 at 15:56
  • `who` just gets you the names of the variables in some local scope – Argyll Mar 28 '18 at 15:58
  • @BetterEnglish Are they return values from a function call? Does the toolbox simply dump 3 new variables into the workspace? – beaker Mar 28 '18 at 15:58
  • @Argyll yes, exactly I want to iterate through a collection of variables with naming convention struct_# and concatenate it using a script. But How to get these variables without using who ? I am working on a script that recuperates these variables from the current workspace – LearnToGrow Mar 28 '18 at 15:59
  • @BetterEnglish: `who` only gets you the variable names. To actually call the variables with their string names, you need to use `eval`. Indeed, interfacing with a third party tool box would be the time to use `eval`. Otherwise, the function is slow as hell and unreliable at times. – Argyll Mar 28 '18 at 16:02
  • @Argyll you can actually use `who` to save all variables of a certain dynamic name, then load them into a struct and read out that struct dynamically, without having to use `eval`. That's a much better solution in my opinion (besides the obvious: write your code right the first time). – Adriaan Mar 28 '18 at 16:16

2 Answers2

1

While I would still highly recommend going back to the point of origin of these dynamically named structures (shame on the toolbox creator if this is actually the output...) and fixing this problem there, there is an approach that does not require the use of eval.

Similar to my approach for a tangentially related question, you can save the desired structures to a temporary *.mat file and take advantage of load's optional structure output to consolidate the data structures for access in a more robust programmatic matter.

For example:

struct_1=struct('field1' ,"str",'field2',0:5);
struct_2=struct('field1' ,"str",'field2',0:10);
struct_3=struct('field1' ,"str",'field2',0:20);

save('tmp.mat', '-regexp', '^struct_');
clear

oldworkspace = load('tmp.mat');
varnames = fieldnames(oldworkspace);
nvars = numel(varnames);

% Get original structure fields
% The subsequent steps assume that all input structs have the same fields
fields = fieldnames(oldworkspace.(varnames{1}));
nfields = numel(fields);

% Initialize output struct
for ii = 1:nfields
    newstruct.(fields{ii}) = [];
end
newstruct(nvars) = newstruct;

for ii = 1:nvars
    for jj = 1:nfields
        newstruct(ii).(fields{jj}) = oldworkspace.(varnames{ii}).(fields{jj});
    end
end

Gives us:

yay

Yay.

sco1
  • 12,154
  • 5
  • 26
  • 48
  • Depending on the size of the data in the workspace, save/load can often fail to preserve the integrity of the data. That was my experience with R2014a. Things could've changed. Although I wouldn't want to try for much longer. – Argyll Mar 28 '18 at 17:23
0

Not sure if your situation absolutely requires dynamic calls of variables by their string names. eval can solve your problem. However, eval is slow. At times, it is unreliable. You can easily have conflicting code as you build various components of your project. And in my experience, there have always been alternatives to eval. In your case, Adriaan outlined an example in his comment.

Since you are only trying to interface with a third-party toolbox, if the number of variables you read is reasonable, you can do the followings with eval.

array=struct;

for i=1:length(structures)
  eval(['array(',num2str(i),').field2=',structures{i},';'])
end

If you know all the structs are named struct_#, you can also do

array=struct;

num=3; % the number of outputs from your toolbox
for i=1:num
  eval(['array(',num2str(i),').field2=struct_',num2str(i),'.field2;'])
end

Note that your field2 values are all arrays. You can't just create an array and expect each "cell" to carry another array value. Arrays, or rather matrices, in Matlab are a special data type. They only take numerical values. (Matrices are highly optimized in Matlab in various ways. That is why we use Matlab.)

Also note that when you set equality of fields of structs, the equality is by reference. There is no deep copy of the data. Modifying one will modify another. (Just beware of this but don't necessarily rely on it. Deciding when to deep copy things is one of the things we let Matlab handle.)

As well, be careful with the result from who. You need to be absolutely clear with what is in the local scope. Above is assuming your result from calling structures=who(str*) is correct for your application.

Argyll
  • 8,591
  • 4
  • 25
  • 46
  • *If*, you must, and really, only then, use `eval`, at least put a huge warning on top of the answer that it's a horrid idea and that the initial code should be restructured to prevent exactly this. See [this answer of mine](https://stackoverflow.com/a/32467170/5211833) and references contained therein on why it is a horrible idea to use dynamic variable names. – Adriaan Mar 28 '18 at 16:14
  • @Adriaan: *Or*, you can take a look at the warning I already gave to the OP. I assume you clarified the intent of the OP through reading those comments since what is currently in the question is unclear by itself? – Argyll Mar 28 '18 at 16:16
  • I read the comments now, apologies, I only saw the answer at first. You're using `eval` for dynamic variable names, and future readers might (and usually won't) read comments, so please repeat the comments in the answer itself for future use. Besides, if you think a question's unclear, please, don't answer, instead flag the question as unclear. This helps you as well in the sense that if the question gets clarified later, it might render current answers invalid. – Adriaan Mar 28 '18 at 16:18
  • That is a good point. Let me do exactly that. – Argyll Mar 28 '18 at 16:18
  • As for the question being unclear, not only it is unclear, there are some things the OP should know when he implements his own solution. That applies to whoever else reading the question. Using `eval` is something to avoid. Being aware of how equality behaves with `struct` can be equally important. I am answering to also clarify those. – Argyll Mar 28 '18 at 16:36