Sorting a custom enumeration

13 views (last 30 days)
Simon Parten
Simon Parten on 4 Dec 2018
Answered: Ron Mikulecky on 15 Jul 2021
For various reasons, I want to be able to define an enumeration. This enumeration will have some properties. I'd also like to be able to use it as a key.
Undefined function 'sort' for input arguments of type 'MyGreatEnum'.
However, methods like 'unique' don't work, which is strange for me, as there is clearly a well defined set of unique enums in a list of enums, that are from the same class.
Any ideas?
i.e. I'd like to do
events
events =
2×1 MyGreatEnum enumeration array
MyGreatEnum1
MyGreatEnum2
unique(events)
Error using sort
Undefined function 'sort' for input arguments of type 'MyGreatEnum'.
Error in unique>uniqueR2012a (line 211)
sortA = sort(a);
Error in unique (line 103)
[varargout{1:nlhs}] = uniqueR2012a(varargin{:});

Accepted Answer

Guillaume
Guillaume on 4 Dec 2018
You should be able to sort your enumeration class if you derive it from a numeric class:
classdef myGreatEnum < double
enumeration
myGreatEnum1 (1)
myGreatEnum2 (2)
myGreatEnum3 (3)
myGreatEnum4 (4)
myGreatEnum5 (5)
myGreatEnum6 (6)
myGreatEnum7 (7)
myGreatEnum8 (8)
myGreatEnum9 (9)
end
end
>> events = [myGreatEnum.myGreatEnum1, myGreatEnum.myGreatEnum5, myGreatEnum.myGreatEnum2, myGreatEnum.myGreatEnum5];
>> unique(events)
ans =
1×3 myGreatEnum enumeration array
myGreatEnum1 myGreatEnum2 myGreatEnum5
Personally, I don't like the way enumerations are implemented in matlab. I would recommend that you use ordinal categorical arrays instead of enums.
>> events = categorical([1 5 2 5], 1:9, compose('MyGreatEnum%d', 1:9), 'Ordinal', true)
events =
1×4 categorical array
MyGreatEnum1 MyGreatEnum5 MyGreatEnum2 MyGreatEnum5
>> unique(events)
ans =
1×3 categorical array
MyGreatEnum1 MyGreatEnum2 MyGreatEnum5
  2 Comments
Guillaume
Guillaume on 4 Dec 2018
Yes, with enumerations you cannot derive from any of the string type. However, since you have a finite number of values, you can just replace your lexicographic ordering by a matching numerical ordering.
If you want a lexicographic ordering, it's easy to get that with categorical arrays. You don't even need it to be ordinal if you're just interested in sort and unique:
>> events = categorical({'event1', 'event2', 'event1'}, compose('event%d', 1:9))
events =
1×3 categorical array
event1 event2 event1
>> unique(events)
ans =
1×2 categorical array
event1 event2
>> sort(events)
ans =
1×3 categorical array
event1 event1 event2

Sign in to comment.

More Answers (1)

Ron Mikulecky
Ron Mikulecky on 15 Jul 2021
I believe I have a better option: overload the built-in sort() function in your enumeration definition.
I built out your enumeration to have a few properties. This is how I ran into your issue.
classdef myGreatEnum
enumeration
% a b c
mGE1 (1, 2, 3)
mGE2 (4, 5, 6)
mGE3 (7, 8, 9)
end
properties
a
b
c
end
methods
function enum = myGreatEnum(varargin)
% Default constructor, sets properties
if(nargin == 3)
% Enumeration definitions are dependent on this order
% don't rearrange this without changing the enumerations
enum.a = varargin{1};
enum.b = varargin{2};
enum.c = varargin{3};
end
end
function [B, idx] = sort(varargin)
% Overloaded function for the builtin sort() function
t = varargin{1};
varargin{1} = string(varargin{1}); % Using varargin allows us to still pass arguments like sort order "descend" into the builtin function
[~, idx] = sort(varargin{:}); % Sort based on the enum names (varargin{1})
B = t(idx);
end
end
end
I chose to sort my enumeration based on the alphabetic enum name. You could just as easily change it to sort on something like peroperty 'a', by using:
function [B, idx] = sort(varargin)
t = varargin{1};
varargin{1} = varargin{1}.a;
[~, idx] = sort(varargin{:}); % Sort based on the enum 'a' properties
B = t(idx);
end
The cool thing is that a structure like this allows you to do things like:
>> myGreatEnum.mGE2.a
ans =
4
or something like:
>> events = [myGreatEnum.mGE2; myGreatEnum.mGE3; myGreatEnum.mGE2]
>> events(1).a
ans =
4
Anyways, now that the sort() function is overloaded to sort on the enum names (mGE1, mGE2, mGE3, etc....), we can call sort(), or even unique():
>> events = [myGreatEnum.mGE2; myGreatEnum.mGE3; myGreatEnum.mGE2] % Creating the array like you had
events =
3x1 myGreatEnum enumeration array
mGE2
mGE3
mGE2
>> sort(events)
ans =
3x1 myGreatEnum enumeration array
mGE2
mGE2
mGE3
>> unique(events)
ans =
2x1 myGreatEnum enumeration array
mGE2
mGE3
we can even get some of the extended sort() capabilites, like passing in arguments, or getting the indices
>> sort(events, 'descend')
ans =
3x1 myGreatEnum enumeration array
mGE3
mGE2
mGE2
>> [B, I] = sort(events, 'descend')
B =
3x1 myGreatEnum enumeration array
mGE3
mGE2
mGE2
I =
2
1
3
Pretty cool! I understand this post may be a little late, but hopefully it will help someone in the future. :)

Products


Release

R2018b

Community Treasure Hunt

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

Start Hunting!