If someone wondered about weather arrays are in depth copied or only reference is copied: reference is copied:
[admin@ap] > { :local a ({}); :local b $a; :local setx do={ :set $x "X"}; ($setx <%% $b); :put $a}
x=X
[admin@ap] >
(RoS 7.16.2)
After :set b $a and manipulating $b changes $a. It proves to me that $b and $a references the same data.
Sertik mentioned a similar behaviour in More about arrays.
There is no Ros version mentioned, but it is no longer the case in Ros 7.16.2.
[admin@ap] > { :local a ({}); :local b $a; :set ($b->"x") "X"; :put $a}
[admin@ap] >
Comparing the two codes, I guess the :set ($b->“x”) makes a copy and then inserts the “x” element. But even this copy will contain references
to array type elements:
[admin@ap] > { :local a {"c"={}}; :local b $a; :set ($b->"x") "X"; :local setx do={ :set $x "X"}; ($setx <%% ($b->"c"));:put [:serialize to=json $a]}
{"c":{"x":"X"}}
[admin@ap] >
:set ($b->“x”) makes a copy of $b array, but manipulating $b->“c” will modify $a.
In conclusion, when you need a real copy of an array do it “yourself”, making copy of each array type element:
:global cpy do={
:if ([:typeof $1]="array") do={
:global cpy
:local result ( ({}),$1) ;# this makes a new array with elements from $1
:foreach k,v in=$result do={
:set ($result->$k) [$cpy $v]
}
:return $result
} else={
:return $1
}
}
[admin@ap] > { :local a {"c"={}}; :local b [$cpy $a]; :set ($b->"x") "X"; :local setx do={ :set $x "X"}; ($setx <%% ($b->"c"));:put [:serialize to=json $a]}
{"c":[]}
[admin@ap] >
To give you a use case where this matters :
There is a complex configuration described with parameters stored in an array, each element is a set of parameters which on their turn can also be arrays:
:local template {
"isp"={ "ether1"="dhcp"; "ether2"={ "pppoe"; "pppoeusernameXXX"; "passwdYYYYY"} };
"vlans"={ "id10"={ "ether3","ether4"} };
"devices"={ "gw"="gateway"; "ap"="accesspoint" }
}
:local networks ({});
:set ($networks->"hq") $template
:set ($networks->"hq"->"devices"->"swServer") "switch" ;# model MMM in rack
:set ($networks->"hq"->"devices"->"swProd") "switch" ;# model MMM at ..
:set ($networks->"hq"->"devices"->"swAcc") "switch" ;# model MMM at ..
:set ($networks->"hq"->"devices"->"swSup") "switch" ;# model MMM at ..
And it goes on and on for a long list, repeating so many times the whole selector, lots of " pairs which I frequently mess up … , it would be nice to make it more simpler, like:
:set $swPord switch ;# model MMM at ..
… in a proper context.
One coud use :set ($networks->“hq”->“devices”) ( ($networks->“hq”->“devices”),{ “swServer”=“switch” …}), but then the comments could not be placed at the end of line .
And here comes the “brilliant” idea:
{
:local devices do={
:set $swServer switch ;# model MMM in rack
:set $swProd switch ;# model MMM at ..
:set $swAcc switch ;# model MMM at ..
:set $swSup switch ;# model MMM at ..
}
($devices <%% ($networks->"hq"->"devices") )
}
It’s much readable, but it comes with a surprise: it also changes $template and any other item built on it.
… and I know, <%% is also undocumented. But what does “documented” mean in the RoS world?