Community discussions

MikroTik App
 
TechnomancerB3
just joined
Topic Author
Posts: 4
Joined: Sat Jan 28, 2023 2:22 am

Adding matched key value pairs to an array dynamically.

Sat Jan 28, 2023 3:18 am

As part of a larger script I'm making I want to acquire the hostname/identity of the device connected to every ethernet interface on a switch and store them in an array for later use. What I essentially need is some way to have an array where the hostnames of neighbors are associated with the interface they are attached to so I can do something like:
:put $idList->ether1
and have it return the hostname.

So far my experimentation has lead me down 2 different paths so I'll document both of those.

Adding referenced array values to the array
Ideally this would work similar to a python dictionary object where I could just append a new key:value pair to the end of the array dynamically and have it work just like a statically defined one, however I've not found a way to get this to work.
:local neighborList [/ip neighbor print as-value]; #Unformatted input
:local idList [{}]

:foreach neighbor in=$neighborList do={
    #Append the interface hostname pair to the end of the list
    :set idList ($idList,{[:tostr ($neighbor->"interface")]=[:tostr ($neighbor->"identity")]})#Problem here
}
:local ifName ether4; #Interface to find
:put $idList; #Debugging; Remove
:put ($idList->$ifName); #Print the desired interface
Despite various combinations of ":tostr", ":parse", converting to a string elsewhere, concatenating additional empty strings, and various other hacks I cannot get it to treat as anything other than a logical comparison thus resulting in an array filled with nothing but "false" 48 times.

I know that the rest of my code works and that the idea is sound because it works as I would expect if I statically set the key like such:
:set idList ($idList,{ether4=[:tostr ($neighbor->"identity")]})
The above actually work to associate the interface and a hostname, only problem is that it can only associate one interface and keeps overwriting it for the whole loop meaning that instead of getting interface ether4's hostname I get ether48's hostname.

String matching
After spending a couple hours messing with the above and reading numerous wiki pages and forum threads I came to the conclusion that the above is either impossible or has no easy solution so I decided to try kludging it by just storing everything as one string and using the ":find" to get the right entry. However my problem is that I can't get ":find" to do anything except exact matching.
:local neighborList [/ip neighbor print as-value]; #Unformatted input
:local idList [{}]

:foreach neighbor in=$neighborList do={
    #Extracts the interface name and hostname and adds them to list
    :set idList ($idList , [:tostr ($neighbor->"interface")]. " " .[:tostr ($neighbor->"identity")])
}
:local ifName ether4; #Interface to find
:put $idList; #Debugging; Remove
:put ($idList->[:find $idList ~ ([:tostr $ifName]."*")]); #Wild card match any string with the right interface in front
I have gotten it to work be replacing the * with an exact copy of the device's hostname but that's not a viable solution since obviously all the devices don't have the same hostname.

Conclusiong
Any help with either of the 2 above paths or a 3rd totally separate way to accomplish what I'm looking for would be much appreciated.
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11967
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Adding matched key value pairs to an array dynamically.

Sat Jan 28, 2023 4:01 pm

As part of a larger script I'm making I want to acquire the hostname/identity of the device connected to every ethernet interface on a switch and store them in an array for later use. What I essentially need is some way to have an array where the hostnames of neighbors are associated with the interface they are attached to so I can do something like:
:put $idList->ether1
and have it return the hostname.
[…]

Ignoring the full wrong syntax (must be ":put ($idList->"ether1")" on this case), is not clear at all, bewcause you do not provide one example for the array,
and also the real final scope of the array.


All this mess for not do something like:
# obtain identity from interface
{
:local int "ether5"
/ip neighbor
:foreach item in=[find where ($interface->0)=$int] do={
    :put [get $item identity]
}
}

# obtain interface from identity
{
:local ide "snom710"
/ip neighbor
:foreach item in=[find where identity=$ide] do={
    :put [get $item interface]
}
}
On both case you can obtain one array, obviously on identity from interface but also from interface from identity
because you can have more than one device with same identity (like one "snom710" for each phone).
And also on interface from identity you obtain one array like "ether5;bri-phone"
 
tomislav91
Member
Member
Posts: 303
Joined: Fri May 26, 2017 12:47 pm

Re: Adding matched key value pairs to an array dynamically.

Sun Jan 29, 2023 2:10 am

While the code provided by @rextended may work for the task, it does not provide a scalable solution as it requires manually entering the desired interface name into the code. A better approach is to use a loop to iterate through each interface and store the interface name and hostname in an array. This allows you to easily access the hostname of the desired interface without having to manually update the code.


For example, you can use the [/ip neighbor print as-value] command to get an array of objects that contains the interface name and the hostname. You can then use a loop to iterate through the array and store the interface name and hostname pair in a new array. This new array can then be used to access the hostname associated with the desired interface name.


For example:

:local neighborList [/ip neighbor print as-value]; 
:local idList [{}]


:foreach neighbor in=$neighborList do={ 
     :set idList ($idList, { 
          ($neighbor->"interface") = ($neighbor->"identity")
     })
}
This will create an array that associates the interface name with the corresponding hostname. You can use the command
:put $idList->$ifName
to get the hostname associated with the desired interface.
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11967
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Adding matched key value pairs to an array dynamically.

Sun Jan 29, 2023 2:36 am

it requires manually entering the desired interface name into the code
Since no one has provided "where" to read the interface name (or hostname),
how do you think the script should be provided which interface (or host) information it wants to have?

you can use the […] command to get an array of objects that contains the interface name and the hostname.
You can then use a loop to iterate through the array and store the interface name and hostname pair in a new array.
This new array can then be used to access the hostname associated with the desired interface name.
Why create one array, from another array for read the value present on the last array, when is already available on first array?
(And why use "print as-value" when [find] can be used?)

:local neighborList [/ip neighbor print as-value]; 
:local idList [{}]

:foreach neighbor in=$neighborList do={ 
     :set idList ($idList, { 
          ($neighbor->"interface") = ($neighbor->"identity")
     })
}
Let's develop your example with real data, you obtain only

result code

idList = false;false;false;false;false;false;false;false;false;false;false;false;false
(13 false, one for each neighbor I have on my router)
You haven't even tried what you randomly wrote.

And about ":local idList [{}]":
An empty array is defined with either [:toarray ""] or ({}).
[{}] is useless, set the variabile as type "nil"
 
tomislav91
Member
Member
Posts: 303
Joined: Fri May 26, 2017 12:47 pm

Re: Adding matched key value pairs to an array dynamically.

Sun Jan 29, 2023 10:45 am

it requires manually entering the desired interface name into the code
Since no one has provided "where" to read the interface name (or hostname),
how do you think the script should be provided which interface (or host) information it wants to have?

you can use the […] command to get an array of objects that contains the interface name and the hostname.
You can then use a loop to iterate through the array and store the interface name and hostname pair in a new array.
This new array can then be used to access the hostname associated with the desired interface name.
Why create one array, from another array for read the value present on the last array, when is already available on first array?
(And why use "print as-value" when [find] can be used?)

:local neighborList [/ip neighbor print as-value]; 
:local idList [{}]

:foreach neighbor in=$neighborList do={ 
     :set idList ($idList, { 
          ($neighbor->"interface") = ($neighbor->"identity")
     })
}
Let's develop your example with real data, you obtain only

result code

idList = false;false;false;false;false;false;false;false;false;false;false;false;false
(13 false, one for each neighbor I have on my router)
You haven't even tried what you randomly wrote.

And about ":local idList [{}]":
An empty array is defined with either [:toarray ""] or ({}).
[{}] is useless, set the variabile as type "nil"
Creating a new array from an existing array is useful when you want to access the values in the first array more easily. For example, the [/ip neighbor print as-value] command returns an array of objects where each object contains the interface name and the hostname. Using a loop to iterate through the array, you can then store the interface name and hostname pair in a new array which makes it easier to access the hostname associated with the desired interface name.


Using [find] without the [print as-value] option returns the index of the object in the array. This is not ideal when you want to access the values stored in the object itself. The [print as-value] option returns an array of objects where each object contains the values for the corresponding entry in the array. This makes it easier to access the values without having to use the object's index.


Using [:toarray ""] or ({}) creates an empty array that can be used to store values. It is important to specify the data type of the array to ensure that it is used correctly. [{}] does not specify a data type and therefore has no effect on the array.
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11967
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Adding matched key value pairs to an array dynamically.

Sun Jan 29, 2023 5:54 pm

You answered to everything except why you write things at random without even trying them.
 
tomislav91
Member
Member
Posts: 303
Joined: Fri May 26, 2017 12:47 pm

Re: Adding matched key value pairs to an array dynamically.

Sun Jan 29, 2023 9:52 pm

You answered to everything except why you write things at random without even trying them.
Writing code without first researching and testing it can lead to errors and unexpected results. I hope I helped :)
 
TechnomancerB3
just joined
Topic Author
Posts: 4
Joined: Sat Jan 28, 2023 2:22 am

Re: Adding matched key value pairs to an array dynamically.

Mon Jan 30, 2023 11:43 pm

Howdy, I'm back with an update.
First, thanks for the responses but none of the above work for what I'm trying to do. As I said in the first post I need an array containing matched key/value pairs where the key is the interface name and the value is the connected host's identity.
I have done some futzing and have gotten it mostly working but have ran into a frustrating roadblock where I have no idea what's going wrong.
This is my code currently:
local neighborList [/ip neighbor print as-value]; #Unformatted input
:global idList [{}]

:foreach neighbor in=$neighborList do={
    #Append the interface hostname pair to the end of the list
    :set $idList ($idList, [:tostr (($neighbor->"interface"->0) ."=". ($neighbor->"identity"))])
}
#:set $idList ($idList, {ether4="SIP-T46U"})    <- This is here for testing, This one works
:local ifName ether4; #Interface to find
:put $idList; #Debugging; Remove
:put ""
:put ($idList->$ifName); #Print the desired interface
This runs and produces an array that looks correct, however in the final line where a specific host's identity is :put'ed doesn't work with the array it creates. This is despite it working when I use the hard coded :set that is currently commented out. The array entry from the hardcoded set and from the dynamic set look identical to me but behave differently for some reason.
This is what the array looks like:
ether4=SIP-T46U;ether6=SIP-T46U;ether6=;ether7=SIP-T46U;ether11=;ether11=SIP-T46U;ether14=;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=SIP-T46U;ether24=;ether25=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=;ether31=SIP-W60B;ether34=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U
Despite this having an entry for ether4 it does not output anything for the final :put

When I uncomment the manual :set idList it creates this array (notice the additionally entry for ether4 at the end):
ether4=SIP-T46U;ether6=SIP-T46U;ether6=;ether7=SIP-T46U;ether11=;ether11=SIP-T46U;ether14=;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=SIP-T46U;ether24=;ether25=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=;ether31=SIP-W60B;ether34=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U;ether4=SIP-T46U
To me the two different entries for ether4 look identical but only the one at the end works with the final :put

I'm not sure what's happening to cause these seemingly identical entries to behave in different ways. if someone could shed some light on this it would be appreciated.
CRS354 ROS v6.49.7
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11967
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Adding matched key value pairs to an array dynamically.

Tue Jan 31, 2023 12:56 am

You create just one array with only string inside,

array of strings code

ether4=SIP-T46U
ether6=SIP-T46U
ether6=
ether7=SIP-T46U
ether11=
ether11=SIP-T46U
ether14=
ether14=SIP-T46U
ether18=SIP-T46U
ether19=SIP-T46U
ether21=SIP-T46U
ether23=SIP-T46U
ether24=SIP-T46U
ether24=
ether25=
ether25=SIP-T46U
ether30=SIP-T46U
ether31=
ether31=SIP-W60B
ether34=SIP-T46U
ether42=SIP-T46U
ether46=SIP-T46U

You do not create one array like field=value, also because ether14 and ether25 have two values and the last overwrite the previous...
You do not create this, and is obvious: ->"ether4" not work!!!

array of fields code

"ether4"="SIP-T46U"
"ether6"="SIP-T46U" replaced by next  "ether6"=""
"ether7"="SIP-T46U"
"ether11"="" replaced by next "ether11"="SIP-T46U"
"ether14"="" replaced by next "ether14"="SIP-T46U"
"ether18"="SIP-T46U"
"ether19"="SIP-T46U"
"ether21"="SIP-T46U"
"ether23"="SIP-T46U"
"ether24"="SIP-T46U" replaced by next  "ether24"=""
"ether25"="" replaced by next "ether25"="SIP-T46U"
"ether30"="SIP-T46U"
"ether31"="" replaced by next "ether31"="SIP-W60B"
"ether34"="SIP-T46U"
"ether42"="SIP-T46U"
"ether46"="SIP-T46U"

This is completely wrong:
    #Append the interface hostname pair to the end of the list
    :set $idList ($idList, [:tostr (($neighbor->"interface"->0) ."=". ($neighbor->"identity"))])

Ignoring the fact than the next item with same interface name replace the previous value, like on ether25 that have 2 devices, you need something like this:
    #Append the interface hostname pair to the end of the list
    :set ($idList->($neighbor->"interface"->0)) ($neighbor->"identity")
 
TechnomancerB3
just joined
Topic Author
Posts: 4
Joined: Sat Jan 28, 2023 2:22 am

Re: Adding matched key value pairs to an array dynamically.

Tue Jan 31, 2023 1:46 am

For my purposes, and for the moment, I don't care that interfaces with multiple identities will get overwritten. I will deal with that when/if it becomes an issue.

Obviously there is something different going on with the array entries internally I just don't know what or why. I can confirm that my method creates an array with many different entries because the ouput from this code:
foreach item in=$idList do={
    :put $item
}

Looks like this:
ether4=SIP-T46U
ether6=SIP-T46U
ether7=SIP-T46U
ether11=
ether11=SIP-T46U
ether14=
ether14=SIP-T46U
ether18=SIP-T46U
ether19=SIP-T46U
ether21=SIP-T46U
ether23=SIP-T46U
ether24=SIP-T46U
ether24=
ether25=
ether25=SIP-T46U
ether30=SIP-T46U
ether31=
ether31=SIP-W60B
ether34=SIP-T46U
ether42=
ether42=SIP-T46U
ether46=SIP-T46U

This line of code you supplied, when slotted into my code and run on my machine, does nothing. At the end of the program the array $idList is still empty.
Ignoring the fact than the next item with same interface name replace the previous value, like on ether25 that have 2 devices, you need something like this:
    #Append the interface hostname pair to the end of the list
    :set ($idList->($neighbor->"interface"->0)) ($neighbor->"identity")
Interestingly when $idList is initialized with a value, any value other than empty array, before entering the loop with your code, the final array will contain some but not all of the entries as such:
ether11=SIP-T46U;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=SIP-60B;ether34=SIP-T46U;ether4=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U;
Additionally the line
:put ($idList->$ifName);
will output SIP-T46U despite still looking for the ether4 entry that, according to the previous line of output, doesn't exist
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 11967
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Adding matched key value pairs to an array dynamically.  [SOLVED]

Tue Jan 31, 2023 1:57 am

Additionally the line
:put ($idList->$ifName);
will output SIP-T46U despite still looking for the ether4 entry that, according to the previous line of output, doesn't exist

Not exist???
This is present on your output:
ether11=SIP-T46U;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=SIP-60B;ether34=SIP-T46U;ether4=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U;

P.S.: you do not read previous posts??? [{}] is wrong, is not one array, is a "nil",
empty array is ({}) or [:toarray ""]
 
TechnomancerB3
just joined
Topic Author
Posts: 4
Joined: Sat Jan 28, 2023 2:22 am

Re: Adding matched key value pairs to an array dynamically.

Tue Jan 31, 2023 2:52 am

This is present on your output:
ether11=SIP-T46U;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=SIP-60B;ether34=SIP-T46U;ether4=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U;

P.S.: you do not read previous posts??? [{}] is wrong, is not one array, is a "nil",
empty array is ({}) or [:toarray ""]
I'll give you those, I might be a bit blind. But it's still missing some of the devices:

Your Code:
ether11=SIP-T46U;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=SIP-60B;ether34=SIP-T46U;ether4=SIP-T46U;ether42=SIP-T46U;ether46=SIP-T46U
My Code:
ether4=SIP-T46U;ether6=SIP-T46U;ether6=;ether7=SIP-T46U;ether11=;ether11=SIP-T46U;ether14=;ether14=SIP-T46U;ether18=SIP-T46U;ether19=SIP-T46U;ether21=SIP-T46U;ether23=SIP-T46U;ether24=SIP-T46U;ether24=;ether25=;ether25=SIP-T46U;ether30=SIP-T46U;ether31=;ether31=SIP-W60B;ether34=SIP-T46U;ether42=;ether42=SIP-T46U;ether46=SIP-T46U
However at this point my main issue has been resolved and I'm pretty sure I can track down whatever problem in my implementation is causing these issues. I might update this thread once I have a final working copy. Thanks for the help Rex.

Who is online

Users browsing this forum: JDF and 24 guests