Problem with if-elseif when conditions contain several strings

15 views (last 30 days)
Hi community,
I noticed a problem with the following code:
year = '2001'
if year == '2000'
a = 1
elseif year == '2006' | year == '2007' | year == '2008' | year == '2009' | year == '2010' | year == '2011' | year == '2014' | year == '2015'
a = 2
end
Somehow this code gives out a = 2, which obviously is not wanted, as the conditions of the elseif line are not met.
If I just set one of the years as the elseif-condition without "|", the code does not give out a = 2, which is fine.
I know that I can use double numbers instead of strings, which works correctly.
So I don't need a solution, but an explanation, why this occurs...
Can you reproduce this false outcome?
Has matlab a general problem with strings in if conditions?
Or do I have an understanding problem how if-elseif works?

Accepted Answer

Stephen23
Stephen23 on 7 Jan 2020
Edited: Stephen23 on 7 Jan 2020
"So I don't need a solution, but an explanation, why this occurs..."
The first thing to do when trying to understand what your code is doing is to actually look at what your code is doing. Lets have a look at your logical comparison:
>> year = '2001';
>> year == '2000'
ans =
1 1 1 0
With character arrays == eq provides an element-wise comparison, so your 4x1 input character arrays return a 4x1 logical array, as the above example shows. Note how the last character of those two vectors are not the same, and hence the corresponding false in the output vector.
Now lets read the if documentation and see what it says about the conditional expression: " An expression is true when its result is nonempty and contains only nonzero elements (logical or real numeric). Otherwise, the expression is false." Consider your 4x1 logical vector: are all of its elements non-zero? (hint no, look at the last one). So your first if condition is NOT true.
You can do the same investigation with your
year == '2006' | year == '2007' | year == '2008' | year == '2009' | year == '2010' | year == '2011' | year == '2014' | year == '2015'
syntax yourself by looking at the resulting logical vector, just ,like I did. And then thinking about how that logical or operator works, and why at the end you will likely just end up with a basically meaningless 4x1 logical vector (which with your example data will contain all true values (and hence does satisfy the if condition)).
"...as the conditions of the elseif line are not met."
Wrong. Now that we have actually read the documentation and looked at your data, we now know that actually the if condition was met: you provided it with a 4x1 logical vector containing only true values.
Eventually you will reach the conclusion that this approach is rather unsuitable for what you are trying to achieve. The correct method is to use strcmp or strcmpi, e.g.:
if strcmp(year,'2006')
or for multiple character vectors use ismember or any wrapped around strcmp:
if ismember(year,{'2006','2007','2008'})
if any(strcmp(year,{'2006','2007','2008'}))
For completeness: if you are using strings (i.e. "") then you can use == to test for equivalence of each string array element, but I would recommend sticking with strcmp as this will give the same expected output for both scalar strings and character vectors, thus making your code more robust.
  1 Comment
Sebastian
Sebastian on 7 Jan 2020
Thanks a lot!
I think I got it.
I falsely assumed, that the code clears/overwrites all the previous condition-tests in the elseif line when i checks the next one.

Sign in to comment.

More Answers (1)

Walter Roberson
Walter Roberson on 7 Jan 2020
year == '2000' is not comparing a single entity to another single entity.
'2001' is not a single entity: it is a character array with size 1 x 4. The test '2001' == '2000' is not checking that the entire "string" is identity: it is a vector test that compares ['2' == '2', '0' == '0', '0' == '0', '1' == '0] returning [true, true, true, false] . Then if [true, true, true, false] considers the condition to be false because at least one entry was false.
Likewise year == '2006' gives [true,true,true,false] and year == '2007' gives [true,true,true,false] . You have an | operation between those two vectors, so the result is [true|true, true|true, true|true, false|false] which gives [true, true, true, false] . And so on through year = '2009', continuing to get those [true, true, true, false] arrays. Then you reach the year == '2010' which is going to give you ['2'=='2','0'=='0,'0'=='1','1'=='0'] which is [true,true,false,false] . You or that with the [true,true,true,false] array you have been building to that point, getting [true|true,true|true,true|false,false|false] which is [true,true,true,false] -- even though the '0' of '01' did not equal the '1' of '10', the or with the previous true value gives true, allowing you to proceed. Now on to the year == '2011' test. ['2'=='2', '0'=='0', '0'=='1', '1'=='1'] which gives [true,true,false,true] . Now or that with the [true,true,true,false] you have been building up, and you get [true|true,true|true,true|false,false|true] and that gives [true,true,true,true] -- although the '0' of '01' does not match the '1' of '11', the or from the previous values gives true, and even though in all previous cases the fourth position was false, the true of comparing the final 1's gives true there, and now you have true in all positions. All of the remaining tests are or tests and they cannot give a false value because you already have all true. So you get to the end of the line with a [true,true,true,true] and if is happy with that because none of the entries are false.
If you want to thing of '2001' as a complete entity that should be compared as a whole to '2000' then you have three choices:
  1. You can switch to using strcmp() instead of == . Or
  2. You can switch to using ismember(year, {'2006', '2007', '2008' , '2009', '2010', '2011', '2014', '2015'}, 'rows') . Or
  3. R2017a or later, you can switch to string entities instead of character vectors:
year = "2001"
if year == "2000"
a = 1
elseif year == "2006" | year == "2007" | year == "2008" | year == "2009" | year == "2010" | year == "2011" | year == "2014" | year == "2015"
a = 2
end
I would suggest to you that using ismember() is easier to maintain.
  1 Comment
Sebastian
Sebastian on 7 Jan 2020
Thank you very much for your detailed explanation!
I assumed that the FALSE in the third position would overwrite the previous TRUE when testing: '2001' == '2011'.

Sign in to comment.

Categories

Find more on Numeric Types in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!