Removing items from a structure array in matlab - matlab

I have a very large structure array in matlab. Suppose, for argument's sake, to simplify the situation, I have something like:
structure(1).name = 'a';
structure(2).name = 'b';
structure(3).name = 'c';
structure(1).returns = 1;
structure(2).returns = 2;
structure(3).returns = 3;
Now suppose I have some condition that comes along and makes me want to delete everything from structure(2) (any and all entries in my structure array). What is a good way to do that?
My solution has been to just set the corresponding fields to [] (e.g. structure(1).name = [];), but that doesn't remove them, that only makes them empty. How do I actually remove them completely from the structure array? Is there a way?

simple if you want to delete element at index i do the following:
i = 3
structure(i) = [];
And that will remove element at index 3.
Example:
st.name = 'text';
st.id = 1524;
arrayOfSt = [st st st st st];
Now:
arrayOfSt =
1x5 struct array with fields:
name
id
If we execute:
arrayOfSt(2) = [];
then the new value of the array of structers will be:
arrayOfSt =
1x4 struct array with fields:
name
id
Try it !

Related

Swap variable names and field names in a set of structs, so the field names become the struct names and vice versa?

I have a set of structs that all have the same field names. For example structs A, B, and C all have field names name and section. Is there a way to rearrange this organization of data so that the data goes from:
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
to this:
name =
A: 'bb'
B: 'joe'
C: 'rob'
section =
A: 199
B: 101
C: 33
The current code I have, for example, operates like this:
% ORIGINAL STRUCTS
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
% CREATE VARIABLE NAMES FOR NEW STRUCTS
oldFNames = fieldnames(A); % old field names
oldVNames{1} = varname(A); % old variable names
oldVNames{2} = varname(B);
oldVNames{3} = varname(C);
% RESTRUCTURE STRUCTS (SWITCH FIELDNAMES AND VARIABLE NAMES)
for j = 1:length(oldFNames)
for k = 1:length(oldVNames)
eval([oldFNames{j} '.' oldVNames{k} ' = ' oldVNames{k} '.' oldFNames{j}]);
end
end
function out = varname(var) % Function to get variable name
out = inputname(1);
end
I find the hack to use the varname function to be not great, and I don't know whether there is a way to make it easily adaptable to the number of variables I have. Any input on how to simplify this procedure would be great. Thanks.
It would be easier to do it all in a function, instead of just using a function to get the input variable name. Here's one solution:
function invert_struct(varargin)
varNames = arrayfun(#inputname, 1:nargin, 'UniformOutput', false); % Get input names
s = [varargin{:}]; % Combine inputs into a structure array
for f = fieldnames(s).' % Loop over fields
assignin('caller', f{:}, cell2struct({s.(f{:})}, varNames, 2));
end
end
And you would call it like so, with however many inputs you like:
invert_struct(A, B, C, ...);
The function uses assignin to create your new variables in the calling function and cell2struct to create a new structure from your field data and old variable names. Note that it also makes use of dynamic field names.

How to extract file names in a loop into a character array?

I have a code like below. I want to generate an array with all the file names and combine with the data that I collected from each file.
DataCC = dir('*-CC.xls'); %select the file type
MeanAreaCC=[];
PlateNameCC=[];
for w = 1: numel(DataCC)
basefilenamedata=DataCC(w).name; %extract the file name
T=readtable(basefilenamedata); %read table in
PlateNameCC=[PlateNameCC basefilenamedata]; %generate the file name array
MeanAreaCC = [MeanAreaCC mean(T.Area)]; %generate the data array
end
x=array2table([PlateNameCC, transpose(MeanAreaCC)],'VariableNames',{'Iso-Condi-Rep','MeanAreaCC'}); %combine two arrays just generated
writetable(x,fullfile(DataFolder,'DataSummary.xls'),'Sheet',1,'Range','A1');
But my code didn't work, as the PlateNameCC is generated as one character but not an array. This came to an error complaining different array size, when I combine PlateNameCC with MeanAreaCC. Could somebody check it for me? Thank you!
There are a couple things wrong here.
First, PlateNameCC = [PlateNameCC basefilenamedata]; is going to create one long string of garbage.
For example:
fnames = dir('*.m')
namelist = [];
for ii = 1:numel(fnames)
namelist = [namelist fnames(ii).name]
end
Gives me:
namelist =
'SOcode.mcallbacksclass.mtestcode.m'
You want to use a string array or a cell array:
fnames = dir('*.m');
namelist1 = string({fnames.name});
namelist2 = {fnames.name};
Which returns:
namelist1 =
1×3 string array
"SOcode.m" "callbacksclass.m" "testcode.m"
namelist2 =
1×3 cell array
{'SOcode.m'} {'callbacksclass.m'} {'testcode.m'}
Second, there's no point trying to concatenate two (very) unlike arrays to create a table when you can just use the table constructor itself:
fnames = dir('*.m');
namelist = string({fnames.name});
data = [1, 2, 3];
T = table(namelist.', data.');
Which gives:
T =
3×2 table
Var1 Var2
__________________ ____
"SOcode.m" 1
"callbacksclass.m" 2
"testcode.m" 3

How to add an array (double) as a field to a struct variable?

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;

Concatenating (sub)fields of structs in a cell array

I have a Matlab object, that is a cell array containting structs that have almost identical structures and I want to programmatically get a (sub)field of the structs of all cell array elements.
For example, we take test
test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};
This will create a cell array with the following structure:
cell-element 1: a --> sub --> 1
cell-element 2: a --> sub --> 2
\-> b --> 1
It can be seen that the elements of test don't have the exact same structure, but similar. How can I get all values of the a.sub fields of the elements of the cell. I can obtain them in this specific problem with
acc=zeros(1,numel(test));
for ii=1:numel(test)
acc(ii) = test{ii}.a.sub;
end
but I can't quite get this method to work in a more general context (ie. having different fields).
You may want to use the function getfield:
%//Data to play with
test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};
%//I'm interested in these nested fields
nested_fields = {'a', 'sub'};
%//Scan the cell array to retrieve the data array
acca = cellfun(#(x) getfield(x, nested_fields{:}), test);
In case your data cannot guarantee that all the elements are the same type and size, then you need to output a cell array instead:
%//Scan the cell array to retrieve the data cell array
accc = cellfun(#(x) getfield(x, nested_fields{:}), test, 'UniformOutput', false);
Later Edit
If one wants to use different sets of nested fields for each cell element then:
%//nested_fields list should have the same size as test
nested_fields = {{'a','sub'}, {'b'}};
accm = cellfun(#(x,y) getfield(x,y{:}), test, nested_fields, 'UniformOutput', false);
Edit: No need for recursion, as shown by #CST-link:s answer; the native getfield function can neatly unfold a cell array of fields as its second argument, e.g. getfield(foo{i}, fields{:}) instead of the call to the recursive function in my old answer below. I'll leave the recursive solution below, however, as it could have some value in the context of the question.
You can build you own recursive version of getField, taking a cell array of fields.
function value = getFieldRec(S,fields)
if numel(fields) == 1
value = getfield(S, fields{1});
else
S = getfield(S,fields{1})
fields{1} = [];
fields = fields(~cellfun('isempty',fields));
value = getFieldRec(S,fields);
end
end
Example usage:
foo = {struct('a',struct('sub',1)), ...
struct('a',struct('sub',2),'b',3), ...
struct('c',struct('bar',7),'u',5)};
accessFields = {'a.sub', 'b', 'c.bar'};
values = zeros(1,numel(foo));
for i = 1:numel(foo)
fields = strsplit(accessFields{i},'.');
values(i) = getFieldRec(foo{i},fields);
end
With the following result
values =
1 3 7
I have found a way to do this using eval:
function out = catCellStructSubField(cellStruct, fieldName)
out = zeros(1,numel(cellStruct));
for ii = 1:numel(cellStruct)
out(ii) = eval(['cellStruct{ii}.' fieldName]);
end
Where it can be used on my test example like this:
catCellStructSubField(test, 'a.sub')
Dynamic field names (cellStruct{ii}.(fieldName)) does not work because I'm accessing sub-fields.
I know eval is often a bad idea. I'm curious for different solutions.

How to use an entry in an array to call a different array entry

I've looked at a few other questions but I couldn't come up with an answer, I'm relatively new to MatLab (but not programming) so I apologize if it's a duplicate.
I'm sure the title isn't very clear so here's an example:
I have an array, say name = ['Jack';'Jill'];. The elements in this array reference other arrays, such as:
Jack.income = 31000;
Jack.car = 1;
Jill.income = 55000;
Jill.car = 0;
Now, I would like to use name to pull data from the other arrays, such as:
data = name(1).income, which should return 31000, or data = name(2).car, which should return 0.
Any help on this would be greatly appreciated.
Thank you very much,
Eric
You'd be better off using an array of structs (or making your own person object perhaps):
people(1).name = 'Jack';
people(1).income = 31000;
people(1).car = 1;
people(2).name = 'Jill';
people(2).income = 55000;
people(2).car = 0;
Now you can generate a list of names like this (see cell arrays and comma separated lists):
names = {people.name};
which you can convert into an index like this (see logical indexing and ismember):
ind = ismember(names, 'Jack');
and then finally extract someone's income:
people(ind).income