Classifying array of strings into the closes regex - ruby

I have the following array:
a
=> ["http://dominio-1-736865.com/path1",
"http://dominio-2-570941.com/path2",
"http://102.160.194.146/path4",
"http://142.231.2.110",
"http://142.231.2.110/path/inventado",
"http://dominio-3-468658.com/path2",
"http://dominio-3-468658.com/path2/path1",
"http://dominio-3-468658.com/path2/path2",
"http://subdominio.dominio-3-468658.com/path2",
"http://www.dominio-3-468658.com/path2",
"http://este-se-repite.re/AP-448055"]
Then I need to group like this:
fqdns
=> ["dominio-1-736865.com", "dominio-2-570941.com", "102.160.194.146", "142.231.2.110", "dominio-3-468658.com", "subdominio.dominio-3-468658.com", "este-se-repite.re"]
getting this =
["http://dominio-1-736865.com/path1"]
["http://dominio-2-570941.com/path2"]
["http://102.160.194.146/path4"]
["http://142.231.2.110", "http://142.231.2.110/path/inventado"]
["http://dominio-3-468658.com/path2", "http://dominio-3-468658.com/path2/path1", "http://dominio-3-468658.com/path2/path2", "http://www.dominio-3-468658.com/path2"]
["http://subdominio.dominio-3-468658.com/path2"]
["http://este-se-repite.re/AP-448055"]
The problem is with subdominio.dominio-3-468658.com, and dominio3-468658.com, that can be in two but I need to meet only in the one that has the subdomain. how can achieve this in ruby
[25] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[1])}
=> [nil, #<MatchData "dominio-2-570941.com">, nil, nil, nil, nil, nil, nil, nil, nil, nil]
[26] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[0])}
=> [#<MatchData "dominio-1-736865.com">, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
[27] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[2])}
=> [nil, nil, #<MatchData "102.160.194.146">, nil, nil, nil, nil, nil, nil, nil, nil]
[28] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[3])}
=> [nil, nil, nil, #<MatchData "142.231.2.110">, #<MatchData "142.231.2.110">, nil, nil, nil, nil, nil, nil]
[29] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[4])}
=> [nil, nil, nil, nil, nil, #<MatchData "dominio-3-468658.com">, #<MatchData "dominio-3-468658.com">, #<MatchData "dominio-3-468658.com">, #<MatchData "dominio-3-468658.com">, #<MatchData "dominio-3-468658.com">, nil]
[30] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[5])}
=> [nil, nil, nil, nil, nil, nil, nil, nil, #<MatchData "subdominio.dominio-3-468658.com">, nil, nil]
[31] pry(#<Notifications::Notification>)> a.map{|d| d.match(fqdns[6])}
=> [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, #<MatchData "este-se-repite.re">]

No need to add regexes here (and create another problem). Use the right tool for the job: URI parsers.
uris = ["http://dominio-1-736865.com/path1",
"http://dominio-2-570941.com/path2",
"http://102.160.194.146/path4",
"http://142.231.2.110",
"http://142.231.2.110/path/inventado",
"http://dominio-3-468658.com/path2",
"http://dominio-3-468658.com/path2/path1",
"http://dominio-3-468658.com/path2/path2",
"http://subdominio.dominio-3-468658.com/path2",
"http://www.dominio-3-468658.com/path2",
"http://este-se-repite.re/AP-448055"]
require 'uri'
uris.group_by{|u| URI(u).host}.values
# => [
# ["http://dominio-1-736865.com/path1"],
# ["http://dominio-2-570941.com/path2"],
# ["http://102.160.194.146/path4"],
# ["http://142.231.2.110", "http://142.231.2.110/path/inventado"], ["http://dominio-3-468658.com/path2", "http://dominio-3-468658.com/path2/path1", "http://dominio-3-468658.com/path2/path2"],
# ["http://subdominio.dominio-3-468658.com/path2"],
# ["http://www.dominio-3-468658.com/path2"],
# ["http://este-se-repite.re/AP-448055"]
#]
Finally, if you want to put domains with "www." in the same bucket with their naked versions:
uris.group_by{|u| URI(u).host.sub(/^www\./, '')}
=> {"dominio-1-736865.com"=>["http://dominio-1-736865.com/path1"],
"dominio-2-570941.com"=>["http://dominio-2-570941.com/path2"],
"102.160.194.146"=>["http://102.160.194.146/path4"],
"142.231.2.110"=>["http://142.231.2.110", "http://142.231.2.110/path/inventado"],
"dominio-3-468658.com"=>
["http://dominio-3-468658.com/path2", "http://dominio-3-468658.com/path2/path1", "http://dominio-3-468658.com/path2/path2", "http://www.dominio-3-468658.com/path2"],
"subdominio.dominio-3-468658.com"=>["http://subdominio.dominio-3-468658.com/path2"],
"este-se-repite.re"=>["http://este-se-repite.re/AP-448055"]}

You can use Enumerable#group_by :
a.group_by {|url| url.match(/http:\/\/([^\/]*)\/?/)[1] }.values
# ["http://dominio-2-570941.com/path2"],
# ["http://102.160.194.146/path4"],
# ["http://142.231.2.110", "http://142.231.2.110/path/inventado"],
# ["http://dominio-3-468658.com/path2",
# "http://dominio-3-468658.com/path2/path1",
# "http://dominio-3-468658.com/path2/path2"],
# ["http://subdominio.dominio-3-468658.com/path2"],
# ["http://www.dominio-3-468658.com/path2"],
# ["http://este-se-repite.re/AP-448055"]]
Regex explanation (without escaping)
http://([^/]*)/?
http:// matches prefix (same in every address)
([^/]*) captures host part - everything but slash /
/? optional slash ending the address

Related

Ruby and RSpec - Test fails when expected output is the same as the method

I know I am missing something simple here. I want to write a test that checks if an array of array has been outputted. The test keeps failing but what the test expects is the same that the method is giving.
connect4.rb
class Board
attr_accessor :board
def make_and_print_board
grid = Array.new(6) { Array.new(6)}
p grid
end
end
connect4_spec.rb
require './lib/connect4'
RSpec.describe Board do
let (:new_board) {Board.new}
it "prints board" do
expect{new_board.make_and_print_board}.to output(
Array.new(6) { Array.new(6)}
).to_stdout
end
end
This is the error...
1) Board prints board
Failure/Error:
expect{new_board.make_and_print_board}.to output(
Array.new(6) { Array.new(6)}
).to_stdout
expected block to output [[nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil]] to stdout, but output "[nil, nil, nil, nil, nil, nil]\n[nil, nil, nil, nil, nil, nil]\n[nil, nil, nil, nil, nil, nil]\n[nil, nil, nil, nil, nil, nil]\n[nil, nil, nil, nil, nil, nil]\n[nil, nil, nil, nil, nil, nil]\n"
What am I missing here? Why isn't it passing? How can I get this test to pass?
The correct way to write this test is to be verbose about your expectation. Test the exact value of what you expect ii to give. p will output a new line so write this way.
RSpec.describe Board do
let (:new_board) {Board.new}
it 'prints board' do
p_output = "[[nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil]]\n"
expect{new_board.make_and_print_board}.to output(p_output).to_stdout
end
end
But you might want to add this spec if you care more about the internals:
it 'it outputs a 6 x 6 2d array' do
expect( new_board.make_and_print_board ).to match_array Array.new(6) { Array.new(6)}
end

String includes substrings from arrays

I have the following:
input string = "1234"
output new_array should = [12, 3, 4, 34]
def string_to_array(string)
noun = ["d", "fgh", "i", "jk", "bcd", "cdef"]
verb = ["cd", "ef", "f", "jkl", "abc"]
ary = (noun+verb).select { |s| string.include? s }
ary.unshift(ary.delete(string)).compact
end
string = "cdef"
string_to_array(string) #=> ["cdef", "d", "cd", "ef", "f"]
I used Array#select, Array#+, Array#delete, Array#unshift, Array#compact and String#include?.
If you want to remove duplicate from output array, use Array#uniq
You could use String#scan (see the last sentence of the doc especially) together with a regular expression. My objective is to demonstrate this approach, not to suggest that it should be the preferred approach.
nouns = ["cdef", "d", "fgh", "i", "jk", "bcd"]
verbs = ["cd", "ef", "f", "jkl", "abc"]
R = Regexp.new (nouns+verbs).map { |s| "(?=(#{s}))?" }.join
#=> /(?=(cdef))?(?=(d))?(?=(fgh))?(?=(i))?(?=(jk))?(?=(bcd))?(?=(cd))?(?=(ef))?(?=(f))?(?=(jkl))?(?=(abc))?/
def string_to_array(str, nouns, strings)
str.scan(R).flatten.compact
end
str = "cdef"
string_to_array(str, nouns, verbs)
#=> ["cdef", "cd", "d", "ef", "f"]
Note:
str.scan(R)
#=> [["cdef", nil, nil, nil, nil, nil, "cd", nil, nil, nil, nil],
# [nil, "d", nil, nil, nil, nil, nil, nil, nil, nil, nil],
# [nil, nil, nil, nil, nil, nil, nil, "ef", nil, nil, nil],
# [nil, nil, nil, nil, nil, nil, nil, nil, "f", nil, nil],
# [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]]
(?=(cdef))? ((?=(cdef)?) also works) is a positive lookahead enclosed in a capture group. It requires that a particular location in the string is immediately followed by the content of the lookahead, but is not part of the match itself. The question mark makes the lookahead optional.

Ruby: For a 2D array,why are nested while loops overwriting elements defined previously? [duplicate]

This question already has answers here:
Creating matrix with `Array.new(n, Array.new)`
(2 answers)
Closed 5 years ago.
Context: Im trying to populate a 2D array with while loops ,after witch I want to try and do it with {} block format. The point is to understand how these two syntax structures can do the same thing.
I have been reviewing this code and scouring the internet for the past hour and Ive decided that I'm simply not getting something, but I dont understand what that is.
The outcome should be
=> [["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"]
=> ..(Sequentially)..
=>["H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"]]
The code is as follows:
char= ('A'..'H').to_a
num= (1..8).to_a
arr=Array.new(8,Array.new(8))
x=0
while x <8
y=0
while y < 8
arr[x][y] = char[x] + num[y].to_s
y+=1
end
x+=1
end
arr
Thank you in advance, I appreciate your patience and time.
####Edit####
The source of the confusion was due to a lack of understanding of the reference concept. Referencing allows us, by using the Array.new(n,Array.new(n)) method scheme, to access the values of the nested arrays that share a reference to their data via their parent array. This question is addressed directly here: Creating matrix with `Array.new(n, Array.new)` . Although I thought it was a issue with my while loops, the problem was indeed how I created the matrix.
Your code is not working due to call to reference. Ruby is pass-by-value, but all the values are references. https://stackoverflow.com/a/1872159/3759158
Have a look at the output
2.4.3 :087 > arr = Array.new(8,Array.new(8))
=> [[nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil, nil, nil, nil]]
2.4.3 :088 > arr[0][0] = 'B'
=> "B"
2.4.3 :089 > arr
=> [["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil], ["B", nil, nil, nil, nil, nil, nil, nil]]
This is happen because of call by object on array object you can see this in effect by a simple example
a = []
b = a
b << 10
puts a => [10]
and very same thing is happening with your code.
Instead of all that try this :
('A'..'H').map{|alph| (1..8).map{|num| "#{alph}#{num}"}}

Ruby 2D Array Assignment Possible Bug?

I'm experiencing the following. I expect only the first sub-element of the first sub array to be assigned "x", not the first element of each sub array. Can anyone explain this behaviour, and perhaps how to work around it? (Note that this may well be expected behaviour, but if it is, it contradicts my expectations.)
x = Array.new(3, Array.new(5))
# => [[nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil]]
x[0][0] # => nil
x[0][0] = "x"
x
# => [["x", nil, nil, nil, nil], ["x", nil, nil, nil, nil], ["x", nil, nil, nil, nil]]
workaround is :
x = Array.new(3) { Array.new(5) }
x[0][0] = 'a'
x # => [["a", nil, nil, nil, nil], [nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil]]
new(size) {|index| block }
Here an array of the given size is created. Each element in this array is created by passing the element’s index to the given block and storing the return value.
Read also Common gotchas
When sending the second parameter, the same object will be used as the value for all the array elements. Since all the Array elements store the same array Array.new(5), changes to one of them will affect them all.
If multiple copies are what you want, you should use the block version which uses the result of that block each time an element of the array needs to be initialized, as I did above.
2d_array = Array.new(rows) { Array.new(columns) }

How can I set elements of a two dimensional array in ruby?

I tried:
1.9.3-p448 :046 > a=Array.new(7){Array.new(7)}
=> [[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil]]
1.9.3-p448 :047 > a[0,0]='a'
=> "a"
1.9.3-p448 :048 > a[0,1]='b'
=> "b"
1.9.3-p448 :049 > a[0,2]='c'
=> "c"
1.9.3-p448 :050 > a[1,0]='d'
=> "d"
1.9.3-p448 :051 > a[1,1]='e'
=> "e"
1.9.3-p448 :052 > a[1,2]='f'
=> "f"
and I got:
1.9.3-p448 :053 > a
=> ["c", "f", [nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil]]
but I wanted
1.9.3-p448 :053 > a
=> ["a","b","c",nil,nil,nil], ["d","e","f", nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, nil]]
In Ruby, as #Daniel points out, accessing multidimensional array elements is done as it is done in, for example, C.
The notation you're attempting to use is from, for example, Pascal, but doesn't work the way you think it does in Ruby. What it does in Ruby is give a start index and a count.
So if you have:
a = ['a','b','c','d','e','f']
Then a[2,3] will be:
['c','d','e']
This is described in the Ruby Array class documentation. If you attempt to assign to it, Ruby will dynamically change the array accordingly. In the above example, if I do this:
a[2,3] = 'h'
Then a will become:
['a','b','h','f']
Or if I do this:
a[2,0] = 'j'
Ruby inserts a value at position 2 and now I get:
['a','b','j','h','f']
In other words, assigning a value to a[2,3] replaced the subarray of three values with whatever I assigned to it. In the case of a two-dimensional array, such as in the original example,
a[0,0] = 'a' # Inserts a new first row of array with value 'a'
a[0,1] = 'b' # Replaces the entire first row of array with 'b'
a[0,2] = 'c' # Replaces the entire first two rows of array with 'c'
a[1,0] = 'd' # Inserts a new first row of array with value 'd'
a[1,1] = 'e' # Replaces the entire second row of array with 'e'
a[1,2] = 'f' # Replaces the entire second and third rows of array with 'f'
Thus, you get the result that you see.
You're currently assigning letters to a range of the outer array. This is the syntax to reference the inner arrays:
a[0][0]='a'

Resources