ItemClass of List

Try to understand how ItemClass works in List:

// test 1
{"some string" "abc"}.select[item.length>5]  // output: {"some string"}

In test 1, ItemClass is String as expected

// test 2
{11 "some string" "abc" true}.select[item.class=String].select[item.length>5)

/* output:
ERROR: The method 'length()' does not exist for an instance of the class '<Boolean|Integer|String>'.
*/

In test 2, it seems ItemClass is a Union type <Boolean|Integer|String>

But if printing out the selected items’ class:

// test 3
[{11 "some string" "abc" true}.select[item.class=String]].do_idx[println("item " idx " class: " item.class)]
// output:
// item 0 class: String
// item 1 class: String

It seems selected items are indeed String, but still output error when invoking methods from String class.

It looks like the ItemClass will be automatically convert into a Union type whenever mixed type found in List, but will not be converted back.

If you have a List with multiple types for its items you may have to cast their type to use a method that is only available for some of the item types.

{11 "some string" "abc" true}.select[[item.class=String] and [item<>String.length>5]]

You can always cast the list once you know that its item type has changed too:

// List is type List{<Boolean|Integer|String>}
!list_multi: {11 "some string" "abc" true}
// If you want to call methods that aren't common across all item types you must cast
list_multi.select[[item.class=String] and [item<>String.length>5]]
// Below won't work - ItemClass_ still <Boolean|Integer|String>
//list_multi%uppercase
// Cast to List{String} now that there are only String items
!list_str: list_multi<>List{String}
// Now you can call String methods on the list directly
list_str%uppercase

[This last example relies on a fix to some too picky runtime checking on the cast. If you run this test prior to getting the updated code - just press “Ignore” if an assert comes up.]

1 Like