问题
I mean to use a struct to hold a "table":
% Sample data
% idx idxstr var1 var2 var3
% 1 i01 3.5 21.0 5
% 12 i12 6.5 1.0 3
The first row contains the field names. Assume I created a struct
ds2 = struct( ...
'idx', { 1, 12 }, ...
'idxstr', { 'i01', 'i12' }, ...
'var1', { 3.5, 6.5 }, ...
'var2', { 21, 1 }, ...
'var3', { 5, 3 } ...
);
How can I retrieve the value for field var2
, for the row corresponding to idxstr
equal to 'i01'
?
Note: that I cannot ensure the length of idxstr
elements will always be 3.
Note 2: Ideally, I would have a method that also works for columns var2
containing strings, or any other type of variable.
PS: I think https://stackoverflow.com/a/35976320/2707864 can help.
回答1:
As I mentioned in the comments, I believe you have the wrong kind of struct for this work. Instead of an array of (effectively single-row) structs, you should instead have a single struct with 'array' fields. (numeric or cell, as appropriate).
E.g.
d = struct(
'idx', [1, 12 ],
'idxstr', {{'i01', 'i12'}},
'var1', [3.5, 6.5],
'var2', [21, 1],
'var3', [5, 3]
);
With this structure, your problem becomes infinitely easier to deal with:
d.var2( strcmp( 'i01', d.idxstr ) )
% ans = 21
This is also far more comparable to R / pandas dataframes functionality (which are also effectively initialised via names and equally-sized arrays like this).
PS. Note carefully the syntax used for the 'idxstr' field: there is an 'outer' cell array with a single element, meaning you're only creating a single struct, rather than an array of structs. This single element happens to be a cell array of strings, where this cell array is of the same size (i.e. has the same number of 'rows') as the numeric arrays.
UPDATE
In response to the comment, adding 'rows' should be fairly straightforward. Here is one approach:
function S = addrow( S, R )
FieldNames = fieldnames( S ).'; NumFields = length( FieldNames );
for i = 1 : NumFields,
S.( FieldNames{i} ) = horzcat( S.( FieldNames{i} ), R{i} );
end
end
Then you can simply do:
d = addrow( d, {5, 'i011', 2.7, 10, 11} );
回答2:
Assuming that idxstr
can be more than 3 characters (there is a shorter version of its always 3 chars), this is the thing I came up with (tested on MATLAB):
logical_index=~cellfun(@isempty,strfind({ds2(:).idxstr},'i01'))
you can access the variables as:
ds2(~cellfun(@isempty,strfind({ds2(:).idxstr},'i01'))).var2;
% using above variable
ds2(logical_index).var2;
You can understand now why MATLAB introduced tables hehe.
回答3:
Maybe you can try the code like below using strcmp
>> [ds2.var2](strcmp('i01',{ds2.idxstr}))
ans = 21
回答4:
I put together function
function el = struct_pick(s, cdata, cnames, rname)
% Pick an element from a struct by column and row name
coldata = vertcat(s.(cdata));
colnames = mat2cell(vertcat(s.(cnames)), ones(1, length(s)));
% This assumes rname is a string
flt = strcmp(colnames, rname);
el = coldata(logical(flt));
endfunction
which is called with
% Pick an element by column and row name
cdata = 'var3';
cnames = 'idxstr';
rname = 'i01';
elem = struct_pick(ds2, cdata, cnames, rname);
and it seems to do the job. I don't know if it is an unnecessarily contrived way of doing it.
Still have to deal with the possibility that the row names are not strings, as with
cnames = 'idx';
rname = 1;
EDIT: If the strings in idxstr
are not all of the same length, this throws error: vertcat: cat: dimension mismatch
.
The answer by Ander Biguri can handle this case.
来源:https://stackoverflow.com/questions/65124441/pick-a-struct-element-by-field-name-and-some-non-sequential-index