Loren on the Art of MATLAB

Turn ideas into MATLAB

This is machine translation

Translated by Microsoft
Mouseover text to see original. Click the button below to return to the English version of the page.

A Best Friend, struct2table 13

Posted by Loren Shure,

Sometimes it's nice to have a best friend. Sometimes I have more than one, context-dependent. I want to share my best friend today for dealing with struct arrays.

Structure arrays are a useful way to collect similar information for a collection of items, even if what gets collected is not always the same size. However, struct arrays can also be cumbersome to work with. Thanks to the table datatype, I can now avoid some of the intricacies of indexing into struct arrays by simply performing a conversion to table first, and then using good, old-fashioned, stands the test of time, MATLAB indexing.

Contents

Some examples

Some examples for working with struct arrays after converting to a table include:

  1. output of dir - (I can then remove dirs, etc. very easily)
  2. jsondecode
  3. regionprops from Image Processing Toolbox - (now includes output to table option)

Let's try it

I'll now get the listing for the blog post directory for one of my guest authors, Alan Weiss.

dirstruct = dir('AlanW')
dirstruct = 
  5×1 struct array with fields:
    name
    folder
    date
    bytes
    isdir
    datenum

And here's a picture from the Windows Explorer

You can readily see that I really have only 3 items, and not the 5 suggested in dirstruct. I can, of course, now look at the names. I extract them via a comma-separated list.

dirstruct.name
ans =
    '.'
ans =
    '..'
ans =
    'Clumping'
ans =
    'FinSymbolic'
ans =
    'Sudoku'

and now you can see that I really don't care about the first two. But I'd have to delete these from the name field, and the folder field, and ... So I definitely can do so, but what a hassle.

Instead let me convert to a table.

dirtable = struct2table(dirstruct)
dirtable =
  5×6 table
        name                   folder                        date             bytes    isdir     datenum  
    _____________    ___________________________    ______________________    _____    _____    __________
    '.'              'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    '..'             'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    'Clumping'       'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    'FinSymbolic'    'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    'Sudoku'         'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05

And now I can eliminate the first 2 rows in the usual way in MATLAB:

dirtable(1:2,:) = []
dirtable =
  3×6 table
        name                   folder                        date             bytes    isdir     datenum  
    _____________    ___________________________    ______________________    _____    _____    __________
    'Clumping'       'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    'FinSymbolic'    'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05
    'Sudoku'         'C:\Work\ArtofMATLAB\AlanW'    '22-Sep-2017 02:50:46'    0        true     7.3696e+05

Have you replaced your use of struct arrays with tables?

I'm wondering if you've used struct arrays before and are now replacing them with tables. And if you have an application where that does not make sense, I'd love to hear about that as well. Let me know here.


Get the MATLAB code

Published with MATLAB® R2017b

Note

Comments are closed.

13 CommentsOldest to Newest

Matt replied on : 1 of 13

You could of course have gotten rid of the unwanted directories by doing: dirstruct(1:2) = [];

I use tables more and more and find them useful. But I am not very successful in trying to start from an empty table and ‘grow’ it row by row. And in some cases I have reverted to using structures as it is awkward when you want the different rows of a column to have different sized arrays.

Peter Verheijen replied on : 2 of 13

Why couldn’t I use dirstruct(1:2)=[] and reach the same result? If my observation is right, one does not need ‘struct2table’. Is there any other application where ‘struct2table’ has an advantage?

Brad Stiritz replied on : 3 of 13

Hi Loren, yes struct2table() is a great conversion function. I recently made good use of it while leveraging a helpful Github repository I found, which is several years old. That code outputs a vector of structs. I convert that output first to table, and then to timetable for further processing. I will be posting my project to File Exchange before long.

In the reverse direction, I find that table2struct() is extremely helpful as well, for display / output of single rows in a table. The usefulness rises in direct proportion to the number of table variables (i.e. columns) ;)

Cris replied on : 4 of 13

I like tables a lot, and they’re more efficiently stored than structs as well. But this example is strange, because I can also do

dirstruct(1:2)=[];

Sure to all of you, I can use the simple deletion syntax you’ve shown. However it’s harder if I want to search for multiple conditions in multiple columns to do the deletion from the struct arrays vs. the tables.

Brad Stiritz replied on : 6 of 13

Matt>I am not very successful in trying to start from an empty table and ‘grow’ it row by row.

Yes, this is a little tricky. I had to get help on this myself from Mathworks support. The developers have indicated as well that this use-case needs better support. For now, a good way to accomplish this is like the following:

% How many rows in table? 
N = 3;

%Initialize all table variables individually first 
VarA = NaN(N,l);
VarB = cell(N,l);

% Initialize table of desired size and types 
T = table(VarA,VarB);

% Iterate through all table rows 
for row = 1 : N

  % Assign values to (i)'th table row 
  T(row,:) = {rand,num2str(rand,2)};
end
Thom replied on : 7 of 13

I used to simply remove the first two entries from dir() until I encountered filenames that begin with ! – rare, but will break dirstruct(1:2)=[];

Converting to a table would allow for easy removal of . and .. by filtering by name rather than looping through the struct and removing entries. Avoiding the problem by filtering using dir(pattern) is better if possible, but being easily able to convert structs to tables is pretty handy elsewhere.

Lara Harris replied on : 8 of 13

Excellent function I hadn’t used before! One issue: applying when you have nested structures. I just tried using after jsondecode and ran into trouble as the struct contains structs contains structs (ever tried parsing the export from a Trello board?! jsondecode is a godsend, but you still need to go several levels deep to extract what you’re looking for). Would be perfect if struct2table converted any inner structs to separate tables, or ignored them, rather than throwing an error. Regardless, I’m sure I will be using this in future.

Guillaume de Sercey replied on : 10 of 13

You could of course have gotten rid of the unwanted directories by doing: dirstruct(1:2) = [];

This if of course how buggy code is written. The assumption that ‘.’ and ‘..’ are always the first two entry is wrong. The ordering is alphabetical (as far as I know this is undocumented, so may not even always be true) and there are several characters that comes before ‘.’, e.g. the ‘+’ matlab package directories.

A truly fullproof way of removing these directory

dirsctruct(ismember(dirstruct.name, {'.', '..'})) = [];

What I don’t understand is why matlab dir returns these completely pointless entries in the first place.

Jan Kappen replied on : 11 of 13

I love tables. They’ve almost replaced (data) structs everywhere for me because of the better visualization, filtering, indexing and so on. If I need structs for compatibility reasons, table2struct is great, especially the “ToScalar” parameter is great to switch between a an array of structs vs struct of arrays!
By the way, you can preallocate tables in R2018, see https://de.mathworks.com/help/matlab/release-notes.html.
What I usually do to grow tables is something like

clear T

for file = each(allFiles)
   in = readtable(file);
   if ~exist('T','variable')
      T = in;
   else
      T = [T; in];
   end
end

That’s not nice but works ok.

But tables are slower than structs, aren’t they?

Thanks!

Jan,

How about this so you don’t need the if statement and the exist check?

T = table.empty;
for file = each(allFiles)
    T = [T; readtable(file)];
end