Community discussions

MikroTik App
 
skyhawk
newbie
Topic Author
Posts: 39
Joined: Thu Jan 14, 2016 10:27 am

Multi-dimensional arrays

Sat Mar 19, 2016 7:29 pm

I'm wondering if it's possible to create a simple multidimensional fixed array and access it in a reasonable fashion....

Something like the following

:local WANStatus (1,1,0; 1,2,0; 1,3,0; 1,4,0; 2,1,0; 2,2,0; 2,3,0; 2,4,0)

:put $WANStatus[1,1]
:set {$WANStatus[1,1] -> 3}

I'm not really grokking how RouterOS handles Arrays, and the documentation in this respect seems rather lacking. (Or my code-fu is too weak to properly parse what's there, just as likely)

If someone could provide a syntactically valid example of the above that would be much appreciated.
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: Multi-dimensional arrays

Sun Mar 20, 2016 12:54 am

:local WANStatus ({{1;1;0};{1;2;0};{1;4;0}});
If my parsing skills are any better (I haven't tested it...)

Regardless, modifying parts of the array is not possible. You need to set the whole variable again.
 
skyhawk
newbie
Topic Author
Posts: 39
Joined: Thu Jan 14, 2016 10:27 am

Re: Multi-dimensional arrays

Sun Mar 20, 2016 11:40 pm

Figured out something that works well enough for my purpose, I think.
It's a bit uglier than I hoped for, but should work well enough.
:local WANStatus {101=2; 102=2; 103=2; 104=2; 201=2; 202=2; 203=2; 204=2}
:local ConnPrefix 100

#Display and change value 4 for connection 1
:put $WANStatus
:local Conn 1
:local value 4
:put ("Existing value = " . ($WANStatus->[:tostr (($ConnPrefix*$Conn)+$value)]))
:set ($WANStatus->[:tostr (($ConnPrefix*$Conn)+$value)]) 5
:put $WANStatus
Can be made cleaner by pre-computing the key string and just using a variable when accessing the array
:local arrayKey [:tostr (($ConnPrefix*$Conn)+$value)]
:set ($WANStatus->$arrayKey) 5
The big, and somewhat unintuitive for me, takeaway is that the key must be a string, even if you're just using decimal numbers.

I don't see an easy way to filter the results of a :foreach through the array, but since this is a fixed array with known size, I can loop through it myself easily enough.
 
skyhawk
newbie
Topic Author
Posts: 39
Joined: Thu Jan 14, 2016 10:27 am

Re: Multi-dimensional arrays

Mon Mar 21, 2016 3:19 am

Almost there, actually I can work around this, but it's not perfect, so I'm going to throw it out there:

Compare this code
# Statically defined array, working properly
{
  :local testArray {11=1; 12=1; 21=1; 22=1}
  :local testRows 2
  :local testCols 2
  :local testRowSize 10
  
  :put $testArray
  :for x from=1 to=$testRows step=1 do={
    :for y from=1 to=$testCols step=1 do={
      :local value ($testArray->[:tostr (($testRowSize * $x) + $y)])
      :put ("Type of row $x column $y is $[:typeof $value] with value $value")
    }
  }
}
Which outputs this:
11=1;12=1;21=1;22=1
Type of row 1 column 1 is num with value 1
Type of row 1 column 2 is num with value 1
Type of row 2 column 1 is num with value 1
Type of row 2 column 2 is num with value 1
To this code which builds the array dynamically
# Dynamically defined array based on the above static array
{
  :local testRows 2
  :local testCols 2
  :local testRowSize 10

  :local testArray

  :for x from=1 to=$testRows step=1 do={
    :for y from=1 to=$testCols step=1 do={
#      :put $testArray
      :local coord (($testRowSize * $x) + $y)
      :if (($x = 1) and ($y = 1)) do={ \
        :set testArray {11=1}
      } else={ \
        :set ($testArray->[:tostr $coord]) 1
      }
    }
  }
  :put $testArray
  :for x from=1 to=$testRows step=1 do={
    :for y from=1 to=$testCols step=1 do={
      :local value ($testArray->[:tostr (($testRowSize * $x) + $y)])
      :put ("Type of row $x column $y is $[:typeof $value] with value $value")
    }
  }
}
And outputs this:
11=1;12=1;21=1;22=1
Type of row 1 column 1 is num with value 1
Type of row 1 column 2 is num with value 1
Type of row 2 column 1 is num with value 1
Type of row 2 column 2 is num with value 1
I've got to use an ugly kludge to start the array, since RouterOS won't let me use variables in a :set statement for an array
:if (($x = 1) and ($y = 1)) do={ \
  :set testArray {11=1}
 else={ \
  :set ($testArray->[:tostr $coord]) 1
}
Is this a shortcoming in the language, or am I an idiot?
 
User avatar
mrz
MikroTik Support
MikroTik Support
Posts: 7042
Joined: Wed Feb 07, 2007 12:45 pm
Location: Latvia
Contact:

Re: Multi-dimensional arrays

Mon Mar 21, 2016 1:10 pm

It is possible to define matrix and edit or get each individual value.
:global a {{1;2;3};{4;5;6}}

:put ($a->0)
1;2;3

:put ($a->0->1)
2

:set ($a->0->1) 9
:environment print 
a={{1; 9; 3}; {4; 5; 6}}
 
skyhawk
newbie
Topic Author
Posts: 39
Joined: Thu Jan 14, 2016 10:27 am

Re: Multi-dimensional arrays

Fri Mar 25, 2016 9:38 am

There's still a bit of magic required to build a matrix programmatically.

I submit the following for whoever may find it useful:
{
  :local rows 4
  :local cols 4
  :local fillVal 0
  :local testArray

  :for x from=0 to=($rows - 1) do={
    :for y from=0 to=($cols - 1) do={
      :if ($y = 0) do={
        :set testArray ($testArray, {{$fillVal}})
      } else={
        :set ($testArray->$x->$y) $fillVal
      }
    }
  }
  :for x from=0 to=($rows - 1) do={
    :put ($testArray->$x)
  }
}
 
PackElend
Member Candidate
Member Candidate
Posts: 269
Joined: Tue Sep 29, 2020 6:05 pm

Re: Multi-dimensional arrays

Sun May 08, 2022 10:46 am

It is possible to define matrix and edit or get each individual value.
:global a {{1;2;3};{4;5;6}}

:put ($a->0)
1;2;3

:put ($a->0->1)
2

:set ($a->0->1) 9
:environment print 
a={{1; 9; 3}; {4; 5; 6}}
can find deal with a matrix?
:put [ :find $a 9 -1 ]
gives an empty result.
 
msatter
Forum Guru
Forum Guru
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Re: Multi-dimensional arrays

Sun May 08, 2022 11:12 am

So what is the lesson learned here? An array is not a string.

Then make it a string: :put [ :find [:tostr $a] 9 -1 ]
 
PackElend
Member Candidate
Member Candidate
Posts: 269
Joined: Tue Sep 29, 2020 6:05 pm

Re: Multi-dimensional arrays

Sun May 08, 2022 2:51 pm

So what is the lesson learned here? An array is not a string.
https://wiki.mikrotik.com/wiki/Manual:Scripting says it can search arrays as well

Then make it a string: :put [ :find [:tostr $a] 9 -1 ]
thx for that :), that would help in "simple" cases.
:global UserDatabase [:toarray ""];

# The key for the array is the username of the user.
# The structure of every user record is as follows:
# password | email | tel | last IP | something | something else |
:set ($UserDatabase->"username1") {"supersecret";"username1@example.com";"+123456789";"0.0.0.0";0;0};
:set ($UserDatabase->"username2") {"megasecret";"username2@example.com";"+987654321";"0.0.0.0";0;0};

# Printing specific property (column) of the data for specific user:
:put ($UserDatabase->"username1"->0);

know I want to:
  1. find the index of the array username2
  2. extract this the username2-array from UserDatabase-array by index
currently, I could your approach to check if the username2-array exists. If so, called it by its name (named array).
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11982
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Multi-dimensional arrays

Sun May 08, 2022 3:35 pm

Remember that everytime you add item or create an array, the main keys are sorted...
:global UserDatabase [:toarray ""];
:set ($UserDatabase->"username4") {"megasecret";"username4@example.com";"+987654321";"0.0.0.0";0;0};
:set ($UserDatabase->"username5") {"megasecret";"username5@example.com";"+987654321";"0.0.0.0";0;0};
:set ($UserDatabase->"username9") {"megasecret";"username9@example.com";"+987654321";"0.0.0.0";0;0};
:set ($UserDatabase->"username3") {"megasecret";"username3@example.com";"+987654321";"0.0.0.0";0;0};
:set ($UserDatabase->"username2") {"megasecret";"username2@example.com";"+987654321";"0.0.0.0";0;0};
:set ($UserDatabase->"username1") {"supersecret";"username1@example.com";"+123456789";"0.0.0.0";0;0};

terminal code

:put $UserDatabase                                                                                   
username1=supersecret;username1@example.com;+123456789;0.0.0.0;0;0;\
username2=megasecret;username2@example.com;+987654321;0.0.0.0;0;0;\
username3=megasecret;username3@example.com;+987654321;0.0.0.0;0;0;\
username4=megasecret;username4@example.com;+987654321;0.0.0.0;0;0;\
username5=megasecret;username5@example.com;+987654321;0.0.0.0;0;0;\
username9=megasecret;username9@example.com;+987654321;0.0.0.0;0;0
I use this "feature" to sort lists...


And about index.... the name itself is the index, a numeric value do not exist...

terminal code

:foreach index,content in=$UserDatabase do={:put $index; :put $content}            
username1
supersecret;username1@example.com;+123456789;0.0.0.0;0;0
username2
megasecret;username2@example.com;+987654321;0.0.0.0;0;0
username3
megasecret;username3@example.com;+987654321;0.0.0.0;0;0
username4
megasecret;username4@example.com;+987654321;0.0.0.0;0;0
username5
megasecret;username5@example.com;+987654321;0.0.0.0;0;0
username9
megasecret;username9@example.com;+987654321;0.0.0.0;0;0

For search inside the array (on username's part)

terminal code

:global searcthis "username1"
:put [:typeof ($UserDatabase->$searcthis)] 
array

:global searcthis "username30"
:put [:typeof ($UserDatabase->$searcthis)] 
nothing
if nothing the index do not exist
If array (or any other variable types stored inside the array filed) the index exist (empty or not)
 
PackElend
Member Candidate
Member Candidate
Posts: 269
Joined: Tue Sep 29, 2020 6:05 pm

Re: Multi-dimensional arrays

Sun May 08, 2022 4:17 pm

thx a lot :)

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], MTNick and 23 guests