Pick a struct element by field name and some non-sequential index

試著忘記壹切 提交于 2021-01-05 08:54:22

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!