フィルターのクリア

Iterate over struct with length>1, with multiple fields

51 ビュー (過去 30 日間)
A Heidebrecht
A Heidebrecht 2024 年 7 月 16 日 17:50
編集済み: Voss 約6時間 前
I have a struct with size>1, and multiple 16 fields.
Each fieldname corresponds to some quantity or property, and the struct stores these properties for 47 different items.
I'm trying to iterate the whole dataset. Preferrably, I'd like to iterate by fieldname and retrieve an array for each filename because within each field name the variable type is uniform.
To illustrate:
K>> teststruct = struct('name', ['Alice', 'Bob', 'Eve'], 'age', {24, 45, 35})
teststruct =
1×3 struct array with fields:
name
age
This shows up nicely as a table in the worspace browser.
However, if I iterate by fieldname, this goes wrong (spurious empty lines removed for readability):
K>> fnames = fieldnames(teststruct);
K>> teststruct.(fnames{1})
ans =
'AliceBobEve'
ans =
'AliceBobEve'
ans =
'AliceBobEve'
What I wanted was an array of all the names, instead I get three answers, of compounded names.
If I do the same with the 'age' field, at least each answer contains only one age, but they're still not in any kind of structure I could use in a code which does not know the size or field names of a struct it needs to deal with. In fact, if I assign the above to a variable, this happens:
K>> names = teststruct.(fnames{1})
names =
'AliceBobEve'
Doing the same with the other field only gives me the first age. One correct piece of data at least, but still not at all what I wanted...
I tried applying the code I found here, which promises to print the contents of an entire struct, but the same happens: I only get the first value of everything.
I know that I could loop over the struct indices first, and access them like this:
value = testsruct(i).(fieldnames(j))
...but then I'd be getting them separately, one by one, instead of getting back the cell array (or any other kind of array) that was used to define the struct in the first place, which is way easier to deal with.
Is that possible somehow?
  3 件のコメント
Stephen23
Stephen23 2024 年 7 月 16 日 19:17
編集済み: Stephen23 2024 年 7 月 16 日 20:16
"Is that possible somehow?"
Of course. Read this if you want to understand comma-separated lists and how to use them:
Also note that square brackets are a concatenation operator (not a "list" operator as some beginners incorrectly think, MATLAB does not have a "list" type). As Voss correctly pointed out, when you concatenate character vectors together you get one character vector:
['Alice', 'Bob', 'Eve']
ans = 'AliceBobEve'
You probably want a cell array:
{'Alice', 'Bob', 'Eve'}
ans = 1x3 cell array
{'Alice'} {'Bob'} {'Eve'}
For example:
S = struct('name', {'Alice', 'Bob', 'Eve'}, 'age', {24, 45, 35})
S = 1x3 struct array with fields:
name age
C = {S.name} % comma-separated list
C = 1x3 cell array
{'Alice'} {'Bob'} {'Eve'}
A Heidebrecht
A Heidebrecht 2024 年 7 月 17 日 8:14
Yes, I've been mired in Python for over a decade, and it kind of shapes expectations, especially if the same syntax appears to do the same thing in some cases... thanks for the extra explanation. Definitely going to read up on this again.

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

採用された回答

Voss
Voss 2024 年 7 月 16 日 18:00
編集済み: Voss 2024 年 7 月 16 日 18:06
teststruct = struct('name', {'Alice', 'Bob', 'Eve'}, 'age', {24, 45, 35})
teststruct = 1x3 struct array with fields:
name age
names = {teststruct.name}
names = 1x3 cell array
{'Alice'} {'Bob'} {'Eve'}
ages = [teststruct.age]
ages = 1x3
24 45 35
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Or, without hard-coding the field name references:
fnames = fieldnames(teststruct);
for ii = 1:numel(fnames)
values = {teststruct.(fnames{ii})};
disp(sprintf('field #%d (%s):',ii,fnames{ii}));
disp(values);
end
field #1 (name):
{'Alice'} {'Bob'} {'Eve'}
field #2 (age):
{[24]} {[45]} {[35]}
In this case each set of field values is stored in a cell array (previously the numeric ages were put into a numeric array) since a cell array can contain any class of variable.
  6 件のコメント
A Heidebrecht
A Heidebrecht 約6時間 前
Thanks! It seems that all such heterogeneous constructs are in cell arrays in the data that I'm dealing with, and it kind of made sense that they would always have to be... so the case you show here would actually also break the code you previously showed. I believe I would have noticed by now if something like this occurred in the data I'm dealing with, though, because isnumeric(S.x) already produces an exception.
Cell arrays should not be an issue because my somecode() routine is already catching them separately and iterating over them, per element.
Anyway, this means that if I want to be quite safe, I should iterate over each element of a struct that I encounter. Good to know... also somewhat unfortunate since Matlab has those nice vectors and matrices ready to use, but I can't make sure that I can treat them as such (unless I know where they come from). I guess I could check whether a non-scalar struct's fields are uniformly shaped, but at that point it would get tedious.
=> I think I'm good now, thanks for the help!
Voss
Voss 約6時間 前
編集済み: Voss 約6時間 前
You're welcome!
By the way, isnumeric(S.x) will throw an error about too many input arguments if S has more than one element (or an error about not enough input arguments if S is empty), because S.x is a comma-separated list (which is used as a set of input arguments to isnumeric in this case).
A way to check whether all values of a given field in a structure array are numeric is:
all(cellfun(@isnumeric,{S.x}))
which works regardless of what each x is in S, because they are put in a cell array ({S.x}), then cellfun iterates over the cells, running isnumeric on each cell's contents, getting true or false for each and concatenating those logicals into a single vector, which is passed to all.

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

その他の回答 (0 件)

カテゴリ

Help Center および File ExchangeStructures についてさらに検索

製品


リリース

R2023b

Community Treasure Hunt

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

Start Hunting!

Translated by