I am writing to suggest an enhancement to the RouterOS scripting language. Currently, the in operator is limited to checking if an IP address belongs to a specific subnet: (:put (10.0.0.1 in 10.0.0.0/24))
While this is useful, expanding the scope of in would significantly improve script readability and performance. I propose making in a universal membership operator, similar to modern programming languages.
Proposed Use Cases:
1. Array (List) Membership Instead of writing a loop to check if a value exists in an array, we could use:
I know all this, and I have my own solutions regarding workarounds.
This is a function I created to reduce the number of if statements I use while writing code. It also solved my problem of finding an element within 1D or 2D arrays, because unfortunately, the methods for finding an element are not the same in 1D or 2D arrays.
So if you knew, why did you purposely write it more complex than it should have been?
I get it, you usually make things up in a more crappy way to try to convince someone else that something's wrong, until you find someone who knows what's really going on and writes the truth.
I don't think this is worth to be added as script language comparison operator. It would be nice to have a function right out of the box. like in_array (haystack, needle) or something.
I am 50:50 about adding an in operator. The idiomatic way on RouterOS seems to be find, even configuration export generates find statements.
I must have missed it in the documentation – it seems like it does not specify what find returns if it does not find a matching result. For example, what is the return value of the following:
Well, I have had a feature request ticket open on this very topic, SUP-208071:
Scripting - in should support array types to test existence in a list
First, thank you for all the recent connivance methods in recent RouterOS script. But one that remains annoying is checking if something is an element in a list-type array. While you can use :find to get an index of element by searching an array, it not that convenient to use to check existance in the array. Specifically it can be either nil or num type, so the comparisons get messy - especially you only need a boolean existence test, not the position.
My thought is the in could be extended to support the raw type on left-hand and the list-type array on right side which return a boolean. Currently in only supports ip types, but the syntax lend itself to array types IMO. For example,
:local evens (2,4,6,8,10)
:if ([:rndnum from=0 to=10] in $evens) do={
:put "random number is even"
} else={
:put "random number is odd"
}
Today, to do same, it’s way more contrived:
:local evens (2,4,6,8,10)
:if ([:find $evens [:rndnum from=0 to=10]] > -1) do={
:put "random number is even"
} else={
:put "random number is odd"
}
Obviously not a huge deal, since it’s possible and one could wrap it in a function…. But since y’all be cleaning up things, I run into needing some “is member of” for array types.
The response was the classic:
Thank you for your request. We will consider this for future implementation.
On your second one, IDK, the problem is a string is not an array... And you can use [:convert to=byte-array] to get a string as an array. So using something to get you array of chars from string be the indirect way to use "in for arrays", since in clouds that the string operators are just poor everywhere, so "in" does not buy you as much as some :regex or :sed or whatever to parse/change strings.
In fact my point was more on string the issue is not a find (or in sugar) is that you cannot do some a search-and-replace, easily. And, use the fuller set of regex like grouping, and/or regex's replace operators. That bigger issues on the string side, IMO. But the in for array at least align it with one-off esoteric case for ip subnets.
So basically +1 on #1, "dont care" on #2 in this thread.
@rextended, I guess I don't see improving the scripting ergonomics is bad, when it does not break older script... And you ignore more complex expression required to handling multiple matcher.
{
:local test {{"Alpha";"D";"I";"P";"Y"};{"B";"Charlie";"H";"O";"X"};{"E";"Foxtrot";"G";"N";"W"};{"J";"K";"L";"M";"V"};{"Q";"R";"S";"Test";"Uniform"}}
:set ($test->"andThis") "isThis"
:put [:tostr $test]
:put ($test~"(^|;)Alpha(\$|;)")
:put ($test~"(^|;)Charlie(\$|;)")
:put ($test~"(^|;)Uniform(\$|;)")
:put ($test~"(^|;)Delta(\$|;)")
:put ($test~"(^|;)andThis=") ; # check if exhist the andThis property (no matter the value)
:put ($test~"=isThis(\$|;)") ; # check if exhist something with isThis value
:put ($test~"(^|;)andThis=isThis(\$|;)") ; # check if exhist the andThis property with exactly isThis value
}
Last time I looked, that was not properly documented. I don't believe that the documentation even specifies that :find returns nil when nothing is found. There are positive examples where a match is found but none that properly explains the case where there is no match. There is only a comparison that hints at :nothing comparing to less than zero.
I wish the scripting pages where on Github where you could create pull requests with improvements…