2

Setup:

I have a 21 x 3 cell array.

The first 2 columns are USUALLY strings or char arrays, but could be 1xn cells of strings or char arrays (if there are multiple alternate strings that mean the same thing in the context of my script). The 3rd element is a number.

I'm looking to return the index of any EXACT match of with a string or char array (but type doesn't have to match) contained in this cell array in column 1, and if column 1 doesn't match, then column 2.

I can use the following:

find(strcmp( 'example', celllist(:,1) ))

find(strcmp( 'example', celllist(:,2) ))

And these will match the corresponding indices with any strings / char arrays in the top level cell array. This won't, of course, match any strings that are inside of cells of strings inside the top level cell array.

Is there an elegant way to match those strings (that is, without using a for, while, or similar loop)? I want it to return the index of the main cell array (1 through 21) if the cells contains the match OR the cell within the cell contains an exact match in ANY of its cells.

4
  • 1
    My guess is you'll have to write this function yourself. Commented Aug 29, 2022 at 15:21
  • @CrisLuengo I wasn't really looking for it to be written for me, but the function I was looking for does exist in MATLAB, it is @cellfun. I forgot about that handy function - though I do wish there was a more efficient answer, that answered my question. Commented Aug 30, 2022 at 19:42
  • I meant to say "no, it doesn't exist, it needs a custom solution". cellfun is just a way to hide the loop, it doesn't prevent it. In my experience, it is slower than writing a loop yourself. Commented Aug 30, 2022 at 20:37
  • @CrisLuengo True, it does still do a loop. Last time I used it in MATLAB I do remember it being much slower. But it was pretty quick when I used it now. I'm on 2021a so maybe they made some optimization improvements? Regardless, it works for the program I needed it for. Commented Sep 8, 2022 at 14:57

1 Answer 1

3

The cellstr function is your friend, since it converts all of the following to a cell array of chars:

  • chars e.g. cellstr( 'abc' ) => {'abc'}
  • cells of chars e.g. cellstr( {'abc','def'} ) => {'abc','def'}
  • strings e.g. cellstr( "abc" ) => {'abc'}
  • string arrays e.g. cellstr( ["abc", "def"] ) => {'abc','def'}

Then you don't have to care about variable types, and can just do an ismember check on every element, which we can assume is a cell of chars.

We can set up a test:

testStr = 'example';
arr = { 'abc', 'def', {'example','ghi'}, "jkl", "example" };
% Expected output is [0,0,1,0,1]

Doing this with a loop to better understand the logic would look like this:

isMatch = false(1,numel(arr)); % initialise output
for ii = 1:numel(arr)          % loop over main array
    x = cellstr(arr{ii});      % convert to cellstr
    isMatch(ii) = any( ismember( testStr, x ) ); % check if any sub-element is match
end

If you want to avoid loops* then you can do this one-liner instead using cellfun

isMatch = cellfun( @(x) any( ismember( testStr, cellstr(x) ) ), arr );
% >> isMatch = [0 0 1 0 1]

So for your case, you could run this on both columns and apply some simple logic to select the one you want

isMatchCol1 = cellfun( @(x) any( ismember( testStr, cellstr(x) ) ), arr(:,1) );
isMatchCol2 = cellfun( @(x) any( ismember( testStr, cellstr(x) ) ), arr(:,2) );

If you want the row index instead of a logical array, you can wrap the output with the find function, i.e. isMatchIdx = find(isMatch);.


*This only avoids loops visually, cellfun is basically a looping device in disguise, but it does save us initialising the output at least.

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent answer. It went above and beyond my expectations. I'm revisiting MATLAB after a few years away and I can't believe I forgot about cellfun. Thank you.
@Trashman glad it hit the mark, no problem!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.