I have a one way linked list:
s=struct('field1', value1, 'field2', value2, 'field3', value3, 'next',[])
s=struct('field1', value3, 'field2', value5, 'field3', value6, 'next', s)
How do I remove the 'next' field so that my linked list becomes a standard structure array, like so?
s(1)=struct('field1', value1, 'field2', value2, 'field3', value3)
s(2)=struct('field1', value3, 'field2', value5, 'field3', value6)
I have tried the rmfield command but I get a 1x1 structure array, but I want, in this example, a 1x2 structure array.
You will want to first aggregate all of your structs together. You could use a recursive function to do this. Then you can call rmfield on the array of structs. You could also even combine the two at the same time.
function S = flattenList(S)
if isempty(S.next)
S = rmfield(S, 'next');
else
S = cat(2, rmfield(S, 'next'), flattenList(S.next));
end
end
As pointed out in the comments, since we are constantly appending data to the output, it can be slow for larger lists. We could determine the expected output size and then fill it within the loop.
This approach would allow you to pre-allocate the output.
function output = flattenList(S)
% Determine how big to make the output
tmp = S;
count = 1;
while ~isempty(tmp.next)
count = count + 1;
tmp = tmp.next;
end
% Pre-allocate the output
output = repmat(rmfield(S(1), 'next'), [1 count]);
tmp = S;
count = 1;
while true
output(count) = rmfield(tmp, 'next');
if isempty(tmp.next)
break;
else
tmp = S.next;
end
end
end
Related
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)) )
I have a structure variable and want to add a field and fill the row with the values of an array (double).
The following code works but isn't very nice. Is there a more elegant way to add a field including values without the use of the mat2cell function or a for loop?
field1 = 1:10
field2 = 4:13
%create struct with field 'start' with 10 values
A = struct('start',mat2cell(field1,1,ones(1,numel(field1))))
%transform field2 to cell
temp = mat2cell(field2,1,ones([numel(field2),1]));
%add field 'end' with 10 values
[A(1:numel(field2)).end] = temp{:};
You can use num2cell rather than mat2cell which will (by default) place each element in it's own cell. Unfortunately, you will still need a temporary variable.
A = struct('start', num2cell(field1));
tmp = num2cell(field2);
[A.end] = tmp{:};
Download "catstruct":
https://www.mathworks.com/matlabcentral/fileexchange/7842-catstruct
clear
field1 = 1:10;
field2 = 4:13;
A = struct('start',num2cell(field1));
A = catstruct(A, struct('end',num2cell(field2)));
For performance you better use single field that contain array:
clear
field1 = 1:10;
field2 = 4:13;
A=[];
A.start = field1;
A.end = field2;
I have a structure with sample being a numeric vector. I would like to replace the numbers in sample by sample/2. However, I do not know how to overcome the following error:
Scalar structure required for this assignment.
Any suggestions are more than welcome.
Example:
field1 = 'event';
value1 = {'A', 'B', 'C', 'D'};
field2 = 'sample';
value2 = 22;
A = struct(field1, value1, field2, value2);
What I want to do:
A.sample = round([A.sample]/2,0);
You should use the deal function to distribute your calculated matrix to the elements of your structure:
sampleCell = num2cell(round([A.sample]/2,0)); % first convert result to cell, to comply with the `deal` syntax
[A.sample] = deal(sampleCell{:});
I have data as a struct with several layers, for example:
data.A.B
The data I want to access is in layer B. But the problem is that field names in B can be different depending on where the data comes from. Therefore I can't just type:
data.A.B.myData
myData is itself a struct
I can use:
fieldnames(data.A)
to find the names, but this doesn't help my much. I would have to write code sections for every possible field name that can occur at this level. And that's just what i trying to avoid.
Is there a way to get down to the data I have (myData) without knowing the field names of B?
Traditionally, you can loop over the fieldnames and perform the search of myData at a specific sub-structure of the struct. However, if you don't know which sub-structure you need to search, then you can perform a recursive algorithm. Below is an example. It will return the first match of myData in the struct or an empty matrix if no match found. The code can be improved to find all matches of myData.
function S2=getmyfield(S1,queriedField)
if isstruct(S1)
% Get all fieldnames of S1
fieldArray=fieldnames(S1);
% Find any match with the queried field. You can also use isfield().
% If there is a match return the value of S1.(queriedField),
% else perform a loop and recurse this function.
matchTF=strcmp(queriedField,fieldArray);
if any(matchTF)
S2=S1.(fieldArray{matchTF});
return;
else
S2=[];
i=0; % an iterator count
while isempty(S2)
i=i+1;
S2=getmyfield(S1.(fieldArray{i}),queriedField);
end
end
else
S2=[];
end
end
Cheers.
You just need a recursive function that checks fieldnames at each level for the structure.
This is roughly what you need (it could be improved to supply the path to the found field).
function [ value, found ] = FindField( rootStruct, fieldName )
%FindField - Find a field with a structure
value = [];
found = 0;
if isstruct( rootStruct )
fields = fieldnames(rootStruct);
for fi=1:length(fields)
if strcmp(fields{fi}, fieldName )
value = rootStruct.(fieldName);
found = true;
return;
end
[value, found ] = FindField( rootStruct.(fields{fi}), fieldName );
if found
return;
end
end
end
end
Usage example:
a.b = 1;
a.b.c = 2;
a.b.d = struct('Index',1,'Special',2);
FindField(a,'d')
ans =
Index: 1
Special: 2
I am trying to compare two cell arrays, 1x160 (a) and 80x1(b). My cell arrays consist of cells which have a number of strings inside. I wanna compare each string ans see if they are equal, then if they are equal, insert to new array, or insert 0 otherwise. I can't find any function for that. I tried 'isequal','strfind' and others. All of them give me next error message:
If any of the input arguments are cell arrays, the first must be a
cell array of strings and the second must be a character array.
Here is my code!
function [inter]=Intersect2(a,b)
int=cell(0);
b2=[b;b];
for i=1:length(a)
if a{i,1}==b2{i,1}(1) ( or 'isequal','strfind')
int{i}=a{i};
else
int{i}=0;
end
end
There are many ways to compare character arrays, one of which is strcmp.
We'll use cellfun as well to avoid looping.
a = {'Dude', 'I', 'am', 'a', 'moose'};
b = {'Well', 'I', 'am', 'a', 'mouse'};
index = cellfun(#strcmp, a, b);
This will compare each element of a against the corresponding element in b, returning a logical array index that is 1 when the elements match and 0 when they do not.
Use this to assign matching values:
int = cell(1, length(a));
int(index) = a(index);
int =
[] 'I' 'am' 'a' []
You can extend this concept to find the set intersection if you wish.