Search code examples
matlabcell-array

How to find an matching element (either number or string) in a multi level cell?


I am trying to search a cell of cell arrays for a matching number (for example, 2) or string ('text'). Example for a cell:

 A = {1 {2; 3};4 {5 'text' 7;8 9 10}};

There is similar question. However, this solution works only, if you want to find a number value in cell. I would need a solution as well for numbers as for strings. The needed output should be 1 or 0 (the value is or is not in the cell A) and the cell level/deepness where the matched element was found.


Solution

  • For your example input, you can match character vectors as well as numbers by replacing ismember in the linked solution with isequal. You can get the depth at which the search value was found by tracking how many times the function has to go round the while loop.

    function [isPresent, depth] = is_in_cell(cellArray, value)
    
        depth = 1;
        f = @(c) isequal(value, c);
        cellIndex = cellfun(@iscell, cellArray);
        isPresent = any(cellfun(f, cellArray(~cellIndex)));
    
        while ~isPresent
            depth = depth + 1;
            cellArray = [cellArray{cellIndex}];
            cellIndex = cellfun(@iscell, cellArray);
            isPresent = any(cellfun(f, cellArray(~cellIndex)));
            if ~any(cellIndex)
                break
            end
        end
    
    end
    

    Using isequal works because f is only called for elements of cellArray that are not themselves cell arrays. Use isequaln if you want to be able to search for NaN values.

    Note this now won't search inside numeric, logical or string arrays:

    >> A = {1 {2; 3};4 {5 'text' 7;8 9 [10 11 12]}};
    >> is_in_cell(A, 10)
    ans =
      logical
       0
    

    If you want that, you can define f as

    f = @(c) isequal(value, c) || isequal(class(value), class(c)) && ismember(value, c);
    

    which avoids calling ismember with incompatible data types, because of the 'short-circuiting' behaviour of || and &&. This last solution is still a bit inconsistent in how it matches strings with character vectors, just in case that's important to you - see if you can figure out how to fix that.