From: Chaim Keren-Tzion Date: 2011-12-14T05:32:21+09:00 Subject: Re: Searching an array for a pattern derived from another array --e89a8f3b9da585d35a04b3ff25d4 Content-Type: text/plain; charset=ISO-8859-1 I must say that Ruby is very elegant! I've been scripting in bash for years but haven't used oop languages nearly as much and this is beyond! The code was needed for a chef cookbook that will set the servers in our network to use the preferred connection to our syslog server. (Can't utilize DNS for this) The three lines below will do all the decision making. servers = node[:syslogserver][:ip].map {|ip| IPAddr.new(ip)} nics = node.network.interfaces.map { |p, f| f[:addresses].keys }.flatten.delete_if{|x| x =~ /(.*):(.*)/ }.map {|ip| IPAddr.new("#{ip}/16")} rsyslog_server = servers.detect {|ip| nics.any? {|nic| nic.include? ip } }.to_s Note: "node[:syslogserver][:ip]" are attributes that I define in a "role" "node.network.interfaces.map { |p, f| f[:addresses].keys }.flatten" will get me the mac addresses and ips on the client that chef is running on then I clean it up by removing the mac addresses (and perhaps the IPV6 addresses that will show up one day?) with "delete_if{|x| x =~ /(.*):(.*)/ }" Thanks guys!! Chaim On Tue, Dec 13, 2011 at 3:13 PM, Rob Biedenharn wrote: > > On Dec 13, 2011, at 5:12 AM, Robert Klemme wrote: > > > On Tue, Dec 13, 2011 at 12:41 AM, Chaim Keren-Tzion > > 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 } } > => # > 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 > > > --e89a8f3b9da585d35a04b3ff25d4--