On Dec 13, 2011, at 5:12 AM, Robert Klemme wrote:

> On Tue, Dec 13, 2011 at 12:41 AM, Chaim Keren-Tzion
> <chaim / intercomp.co.il> wrote:
>> Hi,
>> I've just started using Ruby last week and have a somewhat complex task to
>> complete in a short amount of time and nobody local to ask.
>> 
>> Here is what I'm trying to find:
>> Given two arrays containing IP addresses like the ones below, how would I
>> search for the first element in 'servers' that pattern matches and element
>> in 'nics' where only the first 2 parts of the IP addresses need to match,
>> like 192.168.*.* or 10.14.*.* or 98.139.*.*
>> 
>> servers = Array["192.168.0.251","10.14.0.142","98.139.180.149"]
>> nics = Array["10.10.0.255","173.194.37.16","10.14.0.170"]
>> 
>> I've got some kind of beginning with the code below but I'm stuck. Any
>> hints?
>> 
>> ----------------
>> log_server = "nothing"
>> 
>> until log_server != "nothing" servers.each |thisip| do
>> 
>> # Consider only the first two parts of the 'thisip' IP address (192.168.*.*
>> or 10.14.*.* or 98.139.*.*)
>> # Compare it with each element in 'nics'
>> # Set 'log_server' equal to the first value of 'thisip' that pattern
>> matches an element in 'nics'
>> log_server = thisip
>> 
>> end
>> ----------------
> 
> I would approach this like this:
> 
> 1. Write a method which receives two arguments (servers and nics of course).
> 2. Let the method start by preparing the data, i.e. #map both arrays
> into something which is quicker to match.
> 3. iterate by doing servers_converted.each or maybe
> servers_converted.each_with_index and returning from the method on
> first match.
> 4. return nil at the end of the method (nothing found) or raise an
> exception depending on the wanted semantics
> 
> Now, how to do the matching?  I would not use regular expressions for
> the matching as you really want to match numeric values.  Conversion
> could use ip.scan(/\d+/).map(&:to_i).  You could use Array#[] to
> obtain a two element Array from a four element Array.
> 
> You could as well search for a gem which deals with IP addresses.
> Since the problem is so common chances are that such a beast exists.
> 
> Kind regards
> 
> robert
> 
> -- 
> remember.guy do |as, often| as.you_can - without end
> http://blog.rubybestpractices.com/
> 

Rather than search for a gem, look no further than the standard library and you'll come across the IPAddr library. (It's been in there long before 1.9.3 so you're almost certain to have it.)

$ irb
irb(main):001:0> RUBY_VERSION
=> "1.9.3"
irb(main):002:0> require 'ipaddr'
irb(main):003:0> servers = ["192.168.0.251", "10.14.0.142",  "98.139.180.149"].map {|ip| IPAddr.new(ip)}
irb(main):004:0> nics    = [ "10.10.0.255", "173.194.37.16", "10.14.0.170"   ].map {|ip| IPAddr.new("#{ip}/16")}
irb(main):005:0> log_server = servers.detect {|ip| nics.any? {|nic| nic.include? ip } }
=> #<IPAddr: IPv4:10.14.0.142/255.255.255.255>
irb(main):006:0> log_server.to_s
=> "10.14.0.142"

The /16 added to the mapping of nics is the number of bits in the network mask.

So translating Robert's approach:

1. Write a methods that receives two arguments, servers and nics (as arrays of IP strings). (Optionally, a third argument that gives the bits in the netmask, perhaps defaulting to 16.)
2. map the Strings to IPAddrs
3. detect (find) the first server having an IPAddr that is included in the nic's network.
4. (there is no step 4 ;-)

-Rob