1
votes

I have a cell that has different data types (cell, logical, double, char) except structure. Now I have to write a function that will sort out different data types and output a structure with the field of those data types. The fields have to appear according to their appearance in the cell. So, if the first 'n' element(s) of the cell is double and the (n+1)th element is a char then the first field of the output structure will be double and second field will be char. Below is an example where buildStructure is the function header. sa is the output structure.

ca = {'Moriarty', [true, false], false, {'Pink Suitcase'}}
sa = buildStructure(ca)
  sa=>
  char: {'Moriarty'}
  logical: {[true, false]  [false]}
  cell: {{'Pink Suitcase'}}

I tried it writing a for loop to store different data types in different cells. However, then I am feeling so lost. How can I figure out which data type appeared when? To do that I stored all the classes in a huge string then used 'strfind' to find the place (thus time) of particular data type. But it is making things only complex. Any help will be appreciated! Thanks.

2

2 Answers

2
votes

There are tests for all the data types. see: iscell, ischar, islogial and so on. Their results can be used to index the input.

you can complete this example code:

function out = magicfun(varargin)

il = cellfun(@islogical,varargin);
out = struct('logical',{varargin(il)});
1
votes

You can use class(), isa(), and unique() to do it generically. It's like bdecaf's approach, but that'll require you to write a test for every type and use a variety of functions. Using class and isa will generalize to data of any type using a single test, and will be shorter to write.

Exact Types Only

By comparing class names from class(), you can partition the input in to types based on the exact (most specific) type of each input. The 'stable' option for unique() keeps the output fields in the order of the first occurrences of the types in the input. (In production code I would probably omit the 'stable' so the output ordering is canonicalized based on the type name, but it depends on your requirements.)

function out = break_types(in)
%BREAK_TYPES Partition a cell array based on the types of its contents

inTypes = cellfun(@class, in, 'UniformOutput',false);
[types,ax,bx] = unique(inTypes, 'stable');
out = struct;
for i = 1:numel(types)
    ix = (bx == i);
    out.(types{i}) = in(ix);
end

This is pretty complete and should work with anything that didn't do something silly like override class() or isa().

>> ca = {'Moriarty', [true, false], false, {'Pink Suitcase'}};
>> break_types(ca)
ans = 
       char: {'Moriarty'}
    logical: {[1 0]  [0]}
       cell: {{1x1 cell}}
>> 

Considering Inheritance

If you use isa(), you'll also pick up inheritance relationships for classes. For basic Matlab types, this will give you the same answer as the other implementation. But for classes that inherit from other types, it will categorize them in to all the types they match in the input and required lists.

function out = break_types(in)
%BREAK_TYPES Partition a cell array based on the types of its contents

inTypes = cellfun(@class, in, 'UniformOutput',false);
types = unique(inTypes, 'stable');
out = struct;
for i = 1:numel(types)
    ix = cellfun(@(x) isa(x, types{i}), in);
    out.(types{i}) = in(ix);
end

If you want to ensure that the output struct has an entry for some types even if there are no inputs of that type (so its field would contain an empty array), just append those type names to types before passing them to unique:

requiredTypes = { 'cell' 'int8', 'double', 'float' };
types = unique([inTypes requiredTypes], 'stable');