I’m providing this script in the hopes others may find it useful. I suspect it might be able to be simplified a bit further. The script performs a numeric sort using a bubble sort. Ideally other sorts are faster and better, but given the scripting language and simplicity of the bubble sort it seems to be a good trade off. The script is fairly straight forward. In the first example it takes a numeric array, sorts it, and then prints out the sorted array. The first example is a basic template script that can be used and customized as needed.
The second example is a demonstration of the sort being used to sort an “/ip firewall address-list” containing a list of IP addresses. I use the script to sort IP addresses to make an ordered list to programmatically search for duplicate IP addresses.
Hi
Thank you for this post, But this code has some problem like:
you can not use such code in MK for i from=1 to=($n - 1) to= must be an integer. Instead, :while (i<=n) do={: :set i (i+1); }
Thank you for your feedback. The code is directly posted from working code. That is not to say that the code will work with every version of ROS and on every system. The code was developed and verified to run on a ROS v5.7 system. I have noticed MT scripting does have, to put it nicely, many subtleties in its syntax, and it may be more correct to do it the way that you have outlined. I did some testing and the “for loop” works as expected. There were some variations that I found that were interesting. Anyway, this type of code appears elsewhere including in the MT documentation, for example:
See “Traffic limiting per amount downloaded” section:
Here’s my simple implementation of a recursive merge sort (works as of RouterOS 6.22 where it was implemented and used):
## Merge-sort a simple (non-associative) array:
## NOTE: This only works if each array item can
## be compared using the '<' operator.
/system script environment remove [ find where name="SortList" ];
:global SortList do={
:global SortList;
:local out [:toarray $1];
:local l [:len $out];
:if ($l>1) do={
## Split the list in two, recursively sort, then merge results
## Pick split point index:
:local s ($l/2);
## Recursively sort each half-list:
:local a [$SortList [:pick $out 0 $s]];
:local b [$SortList [:pick $out $s $l]];
## Merge results:
:set out [:toarray ""];
:set l [:len $b];
:local s 0; ## Use $s as index into array $b
:foreach i in=$a do={
:local j [:pick $b $s];
:while ($s<$l && $j<$i) do={
:set out ($out,$j);
:set s ($s+1);
:set j [:pick $b $s];
};
:set out ($out,$i);
};
:while ($s<$l) do={
:set out ($out,[:pick $b $s]);
:set s ($s+1);
};
};
:return $out;
};
Watch out for the global definition and the /system script environment bit (remove it or adapt it as needed). I use this in a global library script on some of my systems.
Example use from the CLI (after the above was defined/executed):
Although this is an old thread, it may be interesting for anyone working with multi-dimensional arrays. I’ve reworked the original bubble sort method into a multi-dimensional sorting script.
You need to supply Array A and the name of the sortkey
# Mikrotik bubble sort implementation for multi-dimensional arrays.
# The sortkey needs to be an INTEGER or an IP address, it does not work for strings and, unfortunately, also not for non-integer numbers
# Tested on RoS 7.16.2
# v2.1
# Select one of the sample Array's A below
:local A {{a="item1"; b=80};{a="item2";b=30};{a="item3";b=20};{a="item4";b=60};{a="item5";b=101};{a="item6";b=3}}
#:local A {{a="item1"; b=192.168.2.5};{a="item2";b=192.169.2.3};{a="item3";b=192.168.2.10};{a="item4";b=192.168.2.245};{a="item5";b=192.168.2.255};{a="item6";b=192.168.2.0}};
# set the sortkey to the integer or IP key name in array A
:local sortkey "b";
:local n [ :len $A ];
:local swapped;
do {
:set swapped false;
:for i from=1 to=($n - 1) do={
:if ( $A->($i-1)->$sortkey > $A->($i)->$sortkey) do={
:set A (([ :pick $A 0 ($i-1) ]), ([ :pick $A $i ($i+1)]), ([ :pick $A ($i-1) ($i)]), ([ :pick $A ($i+1) ( [:len $A] )]));
:set swapped true;
}
}
:set n ($n-1)
} while=($swapped);
:put "After sorted $A \n\r";