Converting Enum values in AL

16 Jul

During my AL training classes, a frequently asked question is how to convert enum values to and from integer or text. So I thought it would be a good idea to share with you what possibilities you have.

First of all, we need to understand the properties of an enum. Let’s look at an enum definition and see how it is constructed.

With these properties, you can always get from ordinal to name or from name to ordinal. But don’t make the mistake that ordinal and index are the same. They are not! Of course, in case you define the ordinal values as 1,2,3,… then they are identical to the index. But what if an enum is extended? Then ordinal value and the index will definitely not be identical anymore.

Example 1: Format

It’s quite common to use the Format command to convert a non-text value to text. You can do the same with the enum variable. Consider this code:

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
    begin
        Level := Level::Gold;
        Message(Format(Level));
    end;

This results in a message with the caption of the enum value:

There are several standard formats properties that you can use. But none of them will convert the enum value to the name. The only value you can get with format is the caption or the ordinal value.

FormatExample
Format(Level, 0, 0)Gold level
Format(Level, 0, 1)Gold level
Format(Level, 0, 2)30
Format(Level, 0, 9)30

Example 2: Getting the ordinal value

There is a different way to get the ordinal value. Instead of using the Format function you can use the method AsInteger(). With the code below, you get 30 in the integer variable.

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        OrdinalValue: Integer;
    begin
        Level := Level::Gold;
        OrdinalValue := Level.AsInteger();
    end;

Example 3: Getting the name value

The real name of an enum can be very useful in case of import or exports. Captions are not the best option. Maybe for export, it can be useful, but for imports, you better use the ordinal or the name. However, there is no method AsText() to directly get the name like you can with AsInteger() for the ordinal. But, there is a way to get it. Even with one line of code.

The enum variable has two properties: Names and Ordinals. Both properties return a list of text. See also the first picture in this post. The trick is to keep in mind that both lists do have equal length and the values at a certain index belong to each other. Let’s have a look at the code:

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        OrdinalValue: Integer;
        Index: Integer;
        LevelName: Text;
    begin
        Level := Level::Gold;
        OrdinalValue := Level.AsInteger();  // Ordinal value = 30
        Index := Level.Ordinals.IndexOf(OrdinalValue);  // Index = 3
        LevelName := Level.Names.Get(Index); // Name = Gold
    end;

This is a safe and generic way to get the name and it will also work with enumextension values.

What about one line of code, the above was three lines? Here you go:

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        OrdinalValue: Integer;
        Index: Integer;
        LevelName: Text;
    begin
        Level := Level::Gold;
        LevelName := Level.Names.Get(Level.Ordinals.IndexOf(Level.AsInteger)); // Name = Gold
    end;

Example 4: Convert from integer

This is an easy one. If you have the integer value (not the index!) then you can convert to the enum with the method FromInteger().

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        OrdinalValue: Integer;
    begin
        OrdinalValue := 30;
        Level := Enum::Level.FromInteger(OrdinalValue);
    end;

As you can see, I use Enum::Level here, because the Level variable itself does not have the FromInteger method. But, for sake of completeness, if I would give the enum variable a different name, then the Enum::Level is not even needed:

    procedure EnumConvertDemo()
    var
        CustLevel: Enum Level;
        OrdinalValue: Integer;
    begin
        OrdinalValue := 30;
        CustLevel := Level.FromInteger(OrdinalValue);
    end;

This code example also indicates that variable naming is important and can influence the scope. I prefer using Enum::Level because that is similar to what we are used to with Database::Customer, Page::”Customer Card”, etc. and it is independent from variable names.

Example 5: Convert from text

With text I mean the name, not the caption. Again, there is no method FromName() like the FromInteger() method. So that is at least consequent. And consequently, we can get the enum value by making use of the Ordinals and Names property.

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        OrdinalValue: Integer;
        Index: Integer;
        LevelName: Text;
    begin
        LevelName := 'Gold';
        Index := Level.Names.IndexOf(LevelName); // Index = 3
        OrdinalValue := Level.Ordinals.Get(Index); // Ordinal value = 30
        Level := Enum::Level.FromInteger(OrdinalValue);
    end;

And again, this can also be written as one line of code:

    procedure EnumConvertDemo()
    var
        Level: Enum Level;
        LevelName: Text;
    begin
        LevelName := 'Gold';
        Level := Enum::Level.FromInteger(Level.Ordinals.Get(Level.Names.IndexOf(LevelName)));
    end;

Finally…

The Ordinals and Names properties are also available at Enum::Level. That means you don’t even have to use the variable for the conversion. If you want to get the ordinal from a name or vice versa, then this also works:

    procedure EnumConvertDemo()
    var
        LevelName: Text;
        OrdinalValue: Integer;
    begin
        LevelName := 'Gold';
        OrdinalValue := Enum::Level.Ordinals.Get(Enum::Level.Names.IndexOf(LevelName));
        LevelName := Enum::Level.Names.Get(Enum::Level.Ordinals.IndexOf(OrdinalValue));
    end;

In APIs it is quite normal to use the name value for enum fields. But you don’t need to write code in APIs to convert to the real enum value, that’s done automatically by the platform.

That’s it! Hope you enjoyed it!

15 thoughts on “Converting Enum values in AL

  1. Really? Didn’t check that. In C/AL or AL? I’m lucky to be able to work with the latest version. 😊

    • You mean how to fix errors in report after convert from C/AL to AL?
      I’m afraid that is not really my expertise.

  2. Hi AJK: we have converted some CAL code with 2 option fields, let says Option X and Option Y. They have almost the same option values: Option X has a,b,c,d. Option Y has b,c,d. Now the option have been converted to Enum objects.

    In code we still use something like Option X := Option Y + 1 and SETRANGE(Option X, Option Y + 1). These lines give warnings and I want to get rid of them. What do you suggest? What is the savest way?

    Can’t trust the index as these may vary when you install extension in a different order right? Should I use the Ordinal value or the Name?

    • As I understand it, the name Option Y determines the value for Option X. In other words, if Option Y = b, then you want to set Option X to b or filter it on b. No matter where ‘b’ is in the list of values of option X. Is that correct?

      The safest way to do this is in two steps:

      Step 1: Get the name of Option Y
      LevelNameY := OptionY.Names.Get(OptionY.Ordinals.IndexOf(OptionY.AsInteger));

      Step 2: Convert LevelNameY to OptionX
      OptionX := Enum::OptionX.FromInteger(OptionX.Ordinals.Get(OptionX.Names.IndexOf(LevelNameY)));

  3. Hi,
    I tried the follow code:

    procedure main();
    var
    Level: Enum Level;
    begin
    Level := EnumConvertDemo(‘Gold’);

    case Level of
    Level::Gold: Message(‘Hello’);
    end;
    end;

    procedure EnumConvertDemo(LevelName: Text): Enum Level;
    begin
    exit(Enum::Level.FromInteger(Level.Ordinals.Get(Level.Names.IndexOf(LevelName))));
    end;

    but return this error:

    ArgumentOutOfRange – An invalid argument was passed to a ‘List’ data type method.

    Could you help me?

    Thank you.
    Roberto.

  4. Do not forget that options are starting with 0 and IndexOf gives Position starting with 1.

  5. Pingback: Deleting enum values – The BC Docs Librarian

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.