Get data from multi-level struct using strings

I need to read data into matlab where a text file specifies the structure and index of the data and another file contains the data itself.
Currently the implementation is done using an eval statement, e.g.
eval(['data.' dataName '=value;'])
I would like to use parfor instead since there are a many large files to process but parfor doesn't allow eval. I tried fixing it by replacing the eval statement with:
data.(dataName) = value;
The issue I'm running into is that dataName specifies multiple levels of a struct, e.g. dataName = 'a.b.c', which gives me an error.
Is there any way to make this work?

 採用された回答

Stephen23
Stephen23 2017 年 10 月 25 日
編集済み: Stephen23 2017 年 10 月 25 日

2 投票

You can use setfield and getfield with nested fields. Unfortunately the help description is quite vague on how to do this, but all you need to do is supply all the fieldnames individually:
>> val = 1;
>> name = 'a.b.c';
>> C = regexp(name,'\.','split');
>> data = setfield(struct(),C{:},val);
>> data.a.b.c
ans = 1
This is much more reliable than using eval, although it is possible that setting fields like this is also not permitted within a parfor loop, but it is worth a try.

7 件のコメント

Jan
Jan 2017 年 10 月 25 日
Perhaps strsplit is faster than regexp('split').
Niklas Nylén
Niklas Nylén 2017 年 10 月 25 日
Thank you! I also have some instances of arrays on one or several levels, e.g. name = 'a.b(n).c(k)'. It seems like it can be solved by
C = {'a' 'b' {n} 'c' {k}}
data = setfield(data, C{:}, val)
Now I just need to figure out a nice way to convert name to c for this case.
Stephen23
Stephen23 2017 年 10 月 25 日
編集済み: Stephen23 2017 年 10 月 25 日
@Niklas Nylén: you can also supply indices to setfield. Have a look at the documentation example, and note this line: "The setfield function supports multiple sets of field and fIndx inputs". Do some experimenting!
Niklas Nylén
Niklas Nylén 2017 年 10 月 25 日
Not necessarily the most elegant solution, but it does the job :-)
name = 'aaa.bb(3).cc(7).de';
data = struct();
val = 5;
C = regexp(name,'((?<=\.?)\w+)|((?<=\()\d+(?=\)))','match');
% C = {'aaa' 'bb' '3' 'cc' '7' 'de'}
for ii = 1:numel(C)
% Convert the indices to cell arrays with numeric values
cNum = str2double(C{ii});
if ~isnan(cNum)
C{ii} = {cNum};
end
end
data = setfield(data, C{:}, val);
Stephen23
Stephen23 2017 年 10 月 25 日
編集済み: Stephen23 2017 年 10 月 25 日
No loop required:
>> name = 'aaa.bb(3).cc(7).de';
>> C = regexp(name,'[\.()]+','split');
>> V = str2double(C);
>> idx = isfinite(V);
>> C(idx) = num2cell(num2cell(V(idx)));
>> data = setfield(struct(), C{:}, val)
Niklas Nylén
Niklas Nylén 2017 年 10 月 26 日
Great! I tried to do this way before but I didn't know about the num2cell function. Thank you.
Niklas Nylén
Niklas Nylén 2017 年 11 月 6 日
So, the final version, which also supports assigning arrays as value:
name = 'aaa.bb(3).cc.de(7)';
data = struct();
val = [5 3 2];
C = regexp(name,'[\.()]+','split');
C = C(~cellfun(@isempty, C));
V = str2double(C);
idx = isfinite(V);
C(idx) = num2cell(num2cell(V(idx)));
if idx(end)
C{end} = [C{end} ':'];
end
data = setfield(data, C{:}, val);

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

ヘルプ センター および File ExchangeStructures についてさらに検索

製品

Community Treasure Hunt

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

Start Hunting!

Translated by