Search code examples
matlabcell-array

How to fix this error extracting empty structure fields from a cell array when using cellfun()


I have a cell array in Matlab, with each cell representing a financial transaction as a structure object (the cell array is earlier imported from a json file).

Here is an example to create a test set which replicates the features of my data, representing 4 individual transactions in this case:

transaction_1 = struct( 'created_on' , '2023-01-22' , 'amount' , 9.60 , 'merchant' , struct('name','ebay') );
transaction_2 = struct( 'created_on' , '2022-11-22' , 'amount' , 6.20 , 'merchant' , struct('name','ikea') );
transaction_3 = struct( 'created_on' , '2019-06-19' , 'amount' , 3.65 , 'merchant' , [] );

transactions = { transaction_1 ; transaction_2 ; transaction_3 };

% Add a fourth which is not "compatible" in dimension with the others
transactions{4} = struct( 'created_on' , '2019-06-19' , 'amount' , 1.25 , 'merchant' , [] , 'location' , 'London');

Each structure in the cell array contains three fields:

  1. A date for the transaction
  2. The amount of the transaction
  3. The third field represents the name of the merchant. If there is no merchant information, then this field is empty. However, if there is info, then this field is another structure with the field 'name'.
  4. Some of the structures also have a 'location' field as well.

The third transaction does not have merchant information in the test set, so transactions{3}.merchant returns empty, while the fourth transaction also has location data.

Now I wish to extract field values from the individual structures into separate cell arrays. The following code works correctly for the dates and amounts

created_on = cellfun(@(x) x.created_on , transactions , 'UniformOutput' , false);
amounts = cellfun(@(x) x.amount , transactions , 'UniformOutput' , false);
merchant_names = cellfun(@(x) x.merchant.name , transactions , 'UniformOutput' , false);

but fails for the merchants because the name doesn't exist in some cases. Is there a nice way to do this without resorting to a loop over all entries?


Solution

  • You could make a helper function to handle the different cases, then just use that in your cellfun.

    merchant_names = cellfun( @(x) structValIfExists(x.merchant, 'name'), transactions, 'uni', 0 );
    
    % >> merchant_names = {'ebay', 'ikea', ''}
    
    function v = structValIfExists( s, fld )
        if isstruct( s ) && isfield( s, fld )
            v = s.(fld);
        else
            v = '';
        end
    end