find string in non-scalar structure matlab - matlab

Here's a non-scalar structure in matlab:
clearvars s
s=struct;
for id=1:3
s(id).wa='nko';
s(id).test='5';
s(id).ad(1,1).treasurehunt='asdf'
s(id).ad(1,2).treasurehunt='as df'
s(id).ad(1,3).treasurehunt='foobar'
s(id).ad(2,1).treasurehunt='trea'
s(id).ad(2,2).treasurehunt='foo bar'
s(id).ad(2,3).treasurehunt='treasure'
s(id).ad(id,4).a=magic(5);
end
is there an easy way to test if the structure s contains the string 'treasure' without having to loop through every field (e.g. doing a 'grep' through the actual content of the variable)?
The aim is to see 'quick and dirtily' whether a string exists (regardless of where) in the structure. In other words (for Linux users): I'd like to use 'grep' on a matlab variable.
I tried arrayfun(#(x) any(strcmp(x, 'treasure')), s) with no success, output:
ans =
1×3 logical array
0 0 0

One general approach (applicable to any structure array s) is to convert your structure array to a cell array using struct2cell, test if the contents of any of the cells are equal to the string 'treasure', and recursively repeat the above for any cells that contain structures. This can be done in a while loop that stops if either the string is found or there are no structures left to recurse through. Here's the solution implemented as a function:
function found = string_hunt(s, str)
c = reshape(struct2cell(s), [], 1);
found = any(cellfun(#(v) isequal(v, str), c));
index = cellfun(#isstruct, c);
while ~found && any(index)
c = cellfun(#(v) {reshape(struct2cell(v), [], 1)}, c(index));
c = vertcat(c{:});
found = any(cellfun(#(c) isequal(c, str), c));
index = cellfun(#isstruct, c);
end
end
And using your sample structure s:
>> string_hunt(s, 'treasure')
ans =
logical
1 % True!

This is one way to avoid an explicit loop
% Collect all the treasurehunt entries into a cell with strings
s_cell={s(1).ad.treasurehunt, s(2).ad.treasurehunt, s(3).ad.treasurehunt};
% Check if any 'treasure 'entries exist
find_treasure=nonzeros(strcmp('treasure', s_cell));
% Empty if none
if isempty(find_treasure)
disp('Nothing found')
else
disp(['Found treasure ',num2str(length(find_treasure)), ' times'])
end
Note that you can also just do
% Collect all the treasurehunt entries into a cell with strings
s_cell={s(1).ad.treasurehunt, s(2).ad.treasurehunt, s(3).ad.treasurehunt};
% Check if any 'treasure 'entries exist
find_treasure=~isempty(nonzeros(strcmp('treasure', s_cell)));
..if you're not interested in the number of occurences

Depending on the format of your real data, and if you can find strings that contain your string:
any( ~cellfun('isempty',strfind( arrayfun( #(x)[x.ad.treasurehunt],s,'uni',0 ) ,str)) )

Related

Correct way of adding to a list from cell array in matlab?

I'm writing some code to split up a 180x2 matlab cell array based on a string in the second column. This string is one of EP,GA,PS,SS or SA. In python I could define empty lists then use conditionals to iterate over the elements of the list and append them to the relevant lists.
The Code
EP=[];
GA=[];
PS=[];
SA=[];
SS=[];
for i=1:size(d),
if strcmp(d(i,2),'EP'),
append(EP,d(i,1))
elseif strcmp(d(i,2),'GA'),
append(GA,i)
elseif strcmp(d(i,2),'PS'),
append(PS,i)
elseif strcmp(d(i,2),'SA'),
append(SA,i)
elseif strcmp(d(i,2),'SS'),
append(SS,i)
end
end
Note that 'd' is a 180x2 cell array that I copied and pasted into matlab rather than import. The general structure of the data however is:
12.9089000000000 'EP'
13.3697000000000 'SA'
13.4335000000000 'EP'
13.5302000000000 'PS'
13.8434000000000 'EP'
14.2583000000000 'EP'
14.8221000000000 'GA'
However when attempting this tactic in matlab I get an error:
Error using append (line 38)
Wrong number of input arguments for obsolete
matrix-based syntax.
Error in Boxplot_All_results (line 12)
append(GA,i)
Could somebody tell me the correct way of doing this in matlab
First of all, please not that d is a cell array. To index the elements of a cell array, use {}. If you index using () like you did, you end up with a small cell array containing only the indexed element(s).
To append in Matlab you basically have two choices:
%concatenate the list with a scalar. Also suitable for two lists.
EP=[EP,d{i,1}] %could also be done using cat
%append to the end
EP(end+1)=d{i,1}
While this fixes the problem, I recommend to implement it on a more generic way:
names={'EP','GA','PS','SA','SS'}
s=struct()
for idx=1:numel(names)
s.(names{idx})=[d{strcmpi(d(:,2),names{idx}),1}]
end
You end up with a struct containing the data you want.
Can you show MatLab code?
How do you append those values to cell array?
You can just create one a = {}
and then append element at the size+1 index. That operation extends your cell array.
Or you can also do a trick like that: a = [a; {value}]
To append elements to a cell array, a correct syntax would be:
for i=1:size(d),
if strcmp(d(i,2),'EP'),
EP = [EP ; d(i,1) ] ; %// append(EP,d(i,1))
elseif strcmp(d(i,2),'GA'),
GA = [GA ; d(i,1) ] ; %// append(GA,i)
but there are more ways, as you can read in the documentation: Add Cells to a Cell Array
There are also more ways to build your final extracted arrays in one assignment instead of having them growing dynamically (mLint will complain about that by the way).
Get the indices of the elements which satisfy your condition then create an array with only the matching elements. For example:
iEP = cellfun( #(c) strcmp(c,'EP') , d(:,2) ) ; %// logical array of indexes where the condition is true
EP = d(iEP,1) ; %// Create "EP" in one assignment - EP is a [cell] array
If you only have numeric values to retrieve in these new variables, may be having a double array instead of a cell array will be convenient :
iGA = cellfun( #(c) strcmp(c,'GA') , d(:,2) ) ; %// logical array of indexes where the condition is true
GA = cell2mat( d(iGA,1) ) ; %// Create "GA" in one assignment - GA is a [double] array
Of course you can bypass the intermediate variable holding the indices:
PS = d( cellfun(#(c)strcmp(c,'PS'),d(:,2)),1) ; %// Create "PS" in one assignment [cell] array
SA = cell2mat(d(cellfun(#(c)strcmp(c,'SA'),d(:,2)),1)) ; %// Create "SA" in one assignment [double] array

Nested structure access using dynamic fieldnames

I'd like to achieve the following using dynamic fieldnames instead of setfield:
Say a struct 'myStruct' has a set of nested structures, i.e.
myStruct.a.b.c = 0
myStruct.a.d = 0
myStruct.a.e.f.g = 0
I want to be able to flexibly set the leaf structure values as follows:
fields = {'a', 'b', 'c'}
paramVal = 1
setfield(myStruct, fields{:}, paramVal)
This works using setfield. Is there a syntax that will do this using dynamic fieldnames? The following obviously doesn't work because the fieldname needs to be a string not an array, but demonstrates what I want:
myStruct.(fields{:}) = 0
Which would be equivalent to:
myStruct.('a').('b').('c') = 0
Recursive solution without eval, ripped from one of my old utility functions:
function s = setsubfield(s, fields, val)
if ischar(fields)
fields = regexp(fields, '\.', 'split'); % split into cell array of sub-fields
end
if length(fields) == 1
s.(fields{1}) = val;
else
try
subfield = s.(fields{1}); % see if subfield already exists
catch
subfield = struct(); % if not, create it
end
s.(fields{1}) = setsubfield(subfield, fields(2:end), val);
end
I guess the try/catch can be replaced with if isfield(s, fields{1}) ..., I don't remember why I coded it like that.
Usage:
>> s = struct();
>> s = setsubfield(s, {'a','b','c'}, 55);
>> s = setsubfield(s, 'a.b.d.e', 12)
>> s.a.b.c
ans =
55
>> s.a.b.d.e
ans =
12
Below is a simple, if crude, solution that works for scalar structs. Applying it to your example,
S=setfld(myStruct,'a.b.c',1)
>> S.a.b.c
ans =
1
In general, though, deeply nested structs are unrecommended.
function S=setfld(S,fieldpath,V)
%A somewhat enhanced version of setfield() allowing one to set
%fields in substructures of structure/object S by specifying the FIELDPATH.
%
%Usage: setfld(S,'s.f',V) will set S.s.f=V
%
%
%%Note that for structure S, setfield(S.s,'f') would crash with an error if
%S.s did not already exist. Moreover, it would return a modified copy
%of S.s rather than a modified copy of S, behavior which would often be
%undesirable.
%
%
%Works for any object capable of a.b.c.d ... subscripting
%
%Currently, only single structure input is supported, not structure arrays.
try
eval(['S.' fieldpath '=V;']);
catch
error 'Something''s wrong.';
end

Matlab; Structure field name with valuable ( = number)

I am trying to assign valuable, which is number and given by for loop, to the name of structure field. For example, I would like to do as following,
A.bx, where A is name of structure(= char), b is part of field name ( = char) and x is valuable given by for loop. A and b is fixed or predefined.
Any comment is appreciated !
genvarname(str,list) generates a valid variable name in str [a string] in which at each iteration value in str is different from the exclusion list
And fieldname(S) returns a list of all the names of the field already in the structure S (use it to create a exclusion list)
Here is a code for what you want:
A = struct ();
for i = 1:5
A.(genvarname ('b', fieldnames (A))) = i;
end
Read about 1. genvarname(str,list) 2. fieldnames(S)
You can name you struct fields using simple sprintf
A = struct()
for ii = 1:10
fn = sprintf('b%d', ii );
A.(fn) = ii; % use the struct
end
I tend to agree with sebastian that suggested using arrays or cells over this type of field naming. In addition to cells and arrays you might find containers.Map to be very versatile and useful.

MATLAB -> struct.field(1:end).field?

Is there a way that I get all the structure subsubfield values of a subfield in one line ? Something like this :
struct.field(1:end).field
If I understand your question aright, you want to collect all the fields of the second-level structure, with the name 'field', into a single output array. It doesn't quite meet your request for a one-liner, but you can do it like this:
a.field1.a = 1;
a.field1.b = 2;
a.field2.a = 3;
a.field2.b = 4;
result = [];
for x = fieldnames(a)'
result = horzcat(result, a.(x{:}).a);
end
The ending value of result is [1 3]
Simple Structure Example
aStruct.subField = struct('subSubField', {1;2;3;4})
So that
aStruct.subField(1).subSubField == 1
aStruct.subField(1).subSubField == 2
Etc. Then the values of the leaf nodes can be obtained via a one-liner as
valueLeafs = [aStruct.subField.subSubField];
Which can be checked via assert(all(valueLeafs == [1,2,3,4])).
Non-Scalar Structure Example
The above one-liner also works when the leaf node values are non-scalar such that they can be horizontally concatenated. For example
bStruct.subField = struct('subSubField', {[1,2];[3,4]})
valueLeafs_b = [bStruct.subField.subSubField]; % works okay
cStruct.subField = struct('subSubField', {[1,2];[3;4]})
valueLeafs_c = [cStruct.subField.subSubField]; % error: bad arg dims
Distinct Class Structure Example
The one-line solution given previously does not work whenever the leaf node values are different class since they cannot - in general, be concatenated. However, use of arrayfun and a tricky anonymous function typically provide the required indexing technique:
dStruct.subField = struct('subSubField', {[1;2];'myString'});
valueLeafs_d = arrayfun(#(x) x.subSubField, dStruct.subField, 'UniformOutput', false)

Iterating through struct fieldnames in MATLAB

My question is easily summarized as: "Why does the following not work?"
teststruct = struct('a',3,'b',5,'c',9)
fields = fieldnames(teststruct)
for i=1:numel(fields)
fields(i)
teststruct.(fields(i))
end
output:
ans = 'a'
??? Argument to dynamic structure reference must evaluate to a valid field name.
Especially since teststruct.('a') does work. And fields(i) prints out ans = 'a'.
I can't get my head around it.
You have to use curly braces ({}) to access fields, since the fieldnames function returns a cell array of strings:
for i = 1:numel(fields)
teststruct.(fields{i})
end
Using parentheses to access data in your cell array will just return another cell array, which is displayed differently from a character array:
>> fields(1) % Get the first cell of the cell array
ans =
'a' % This is how the 1-element cell array is displayed
>> fields{1} % Get the contents of the first cell of the cell array
ans =
a % This is how the single character is displayed
Since fields or fns are cell arrays, you have to index with curly brackets {} in order to access the contents of the cell, i.e. the string.
Note that instead of looping over a number, you can also loop over fields directly, making use of a neat Matlab features that lets you loop through any array. The iteration variable takes on the value of each column of the array.
teststruct = struct('a',3,'b',5,'c',9)
fields = fieldnames(teststruct)
for fn=fields'
fn
%# since fn is a 1-by-1 cell array, you still need to index into it, unfortunately
teststruct.(fn{1})
end
Your fns is a cellstr array. You need to index in to it with {} instead of () to get the single string out as char.
fns{i}
teststruct.(fns{i})
Indexing in to it with () returns a 1-long cellstr array, which isn't the same format as the char array that the ".(name)" dynamic field reference wants. The formatting, especially in the display output, can be confusing. To see the difference, try this.
name_as_char = 'a'
name_as_cellstr = {'a'}
You can use the for each toolbox from http://www.mathworks.com/matlabcentral/fileexchange/48729-for-each.
>> signal
signal =
sin: {{1x1x25 cell} {1x1x25 cell}}
cos: {{1x1x25 cell} {1x1x25 cell}}
>> each(fieldnames(signal))
ans =
CellIterator with properties:
NumberOfIterations: 2.0000e+000
Usage:
for bridge = each(fieldnames(signal))
signal.(bridge) = rand(10);
end
I like it very much. Credit of course go to Jeremy Hughes who developed the toolbox.