Nested Ruby List Generation - ruby

I'm trying to generate a list that looks like this:
list = [[1, 1], [2, 2], [3, 3], [4, 4], ... [25, 25]]
Is there an easy way to accomplish this with something similar to range?
Update: Looks like .zip wins
.map time elapsed 1184.344 milliseconds
.zip time elapsed 706.23 milliseconds
Test:
beginning_time = Time.now
(1..2500000).map { |i| [i,i] }
end_time = Time.now
puts "Time elapsed #{(end_time - beginning_time)*1000} milliseconds"
beginning_time = Time.now
(1..2500000).zip 1..25
end_time = Time.now
puts "Time elapsed #{(end_time - beginning_time)*1000} milliseconds"

Try this:
(1..25).map { |i| [i,i] }
Output:
[[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25]]

(1..25).zip 1..25
#=> [[1, 1], [2, 2], [3, 3], [4, 4] ...

Try this:
Array.new(25){|i| [i+1, i+1]}

Ok, 2 hours late, and, just to be different...
[[*1..25], [*1..25]].transpose
# => [[1, 1], [2, 2], [3, 3], [4, 4], ...
...or...hehe...
([[*1..25]]*2).transpose

Related

Integer to array of chunks

I need to turn an integer into an array of intervals. For example,
number_to_steps(number: 10, step: 3)
# => [[0, 2], [3, 5], [6, 8], [9, 9]]
number_to_steps(number: 7, step: 2)
# => [[0, 1], [2, 3], [4, 5], [6, 6]]
number_to_steps(number: 8, step: 2)
# => [[0, 1], [2, 3], [4, 5], [6, 7]]
I tried:
def number_to_ranges(number:, size:)
chunks = ((number - 1) / size.to_f).ceil
(0..chunks - 1).map do |index|
from = index * size
to = (index + 1) * size - 1
[ from, to > number ? number : to ]
end
end
But it doesn't work properly. For example,
number_to_ranges(number: 14, step: 4)
[[0, 3], [4, 7], [8, 11], [12, 14]]
should not go to 14.
Any idea?
An alternative (to your method) would be to use each_slice.
def number_to_steps(number:, step:)
(0...number).each_slice(step).map { |arr| [arr.first, arr.last] }
end
Tests:
number_to_steps(number: 10, step: 3)
#=> [[0, 2], [3, 5], [6, 8], [9, 9]]
number_to_steps(number: 7, step: 2)
#=> [[0, 1], [2, 3], [4, 5], [6, 6]]
number_to_steps(number: 8, step: 2)
#=> [[0, 1], [2, 3], [4, 5], [6, 7]]
def number_to_steps(number:, size:)
(0..number-1).step(size).map { |i| [i, [i+size, number].min-1] }
end
number_to_steps(number: 10, size: 3)
#=> [[0, 2], [3, 5], [6, 8], [9, 9]]
number_to_steps(number: 7, size: 2)
#=> [[0, 1], [2, 3], [4, 5], [6, 6]]
number_to_steps(number: 8, size: 2)
#=> [[0, 1], [2, 3], [4, 5], [6, 7]]
There are two places where you need to change your method number_to_ranges.
When calculating the number of chunks, change number - 1 to number.
When finding the last element for a chunk, do number - 1 instead of number.
This is what the final code should look like:
def number_to_ranges(number:, size:)
chunks = (number / size.to_f).ceil
(0...chunks).map do |index|
from = index * size
to = (index + 1) * size - 1
[from, [to, number - 1].min]
end
end
number_to_ranges(number: 10, size: 3)
=> [[0, 2], [3, 5], [6, 8], [9, 9]]
number_to_ranges(number: 7, size: 2)
=> [[0, 1], [2, 3], [4, 5], [6, 6]]
number_to_ranges(number: 8, size: 2)
=> [[0, 1], [2, 3], [4, 5], [6, 7]]
number_to_ranges(number: 14, size: 4)
=> [[0, 3], [4, 7], [8, 11], [12, 13]]

Combining arrays in Ruby

The following array contains two arrays each having 5 integer values:
[[1,2,3,4,5],[6,7,8,9,10]]
I want to combine these in such a way that it generates five different arrays by combining values of both arrays at index 0,1.. upto 4.
The output should be like this:
[[1,6],[2,7],[3,8],[4,9],[5,10]]
Is there any simplest way to do this?
What about transpose method?
a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
#=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
a.transpose
#=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
this method also can help you in future, as example:
a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]
#=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]
a.transpose
#=> [[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]
a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
a.first.zip(a.last)
If you're sure your sub arrays have the same length, you can use Array#transpose :
[[1,2,3,4,5],[6,7,8,9,10]].transpose
#=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
As a bonus, it works fine with more than 2 arrays :
[[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]].transpose
#=> [[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]
If you're not sure your sub arrays have the same length :
[[1,2,3,4,5],[6,7,8,9], [10,11]].reduce(&:zip).map(&:flatten)
#=> [[1, 6, 10], [2, 7, 11], [3, 8, nil], [4, 9, nil], [5, nil, nil]]
Using transpose in this example would throw an IndexError.
Using parallel assignment:
a, b = [[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]]
a.zip b #=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

method that generate numbers unless remaining of quotient is 0 ruby

I want to create a little method that generates 2 random numbers (num1,num2) (let's say from 1 to 100) but these numbers need to be divisable together
put in more mathematics terms, I would like to generate 2 random numbers where the remaining of the quotient is 0
I did this:
def operation
operators = [:/, :+, :-, :*]
#operation = operators.sample
end
def result(num1, num2)
if #operation == :/
unless num1 % num2 != 0
numbers
else
#result = #num1.send(#operation, #num2)
end
else
#result = #num1.send(#operation, #num2)
end
end
def numbers
#num1 = rand(1..100)
#num2 = rand(1..100)
end
numbers
result(#num1,#num2)
the idea is that unless the remaining of the two numbers is 0 it returns the number method again. I believe there is some iteration problem over here, as I receive an error '`result': nil is not a symbol nor a string (TypeError)'
thx
You're not calling the method operation, so #operation will be nil. So it is invalid to use #operation as the method name when you call send.\
I suggest you refactor things so that you aren't storing a bunch of state in instance variables. Instead, pass any data into functions using function arguments.
To ensure that there is no bias in the sampling, you must first construct the universe of all pairs that satisfy the specified conditions. You can do that as follows.
The numbers can range from 1 to a specified maximum, say
mx = 50
For this value of mx the list of valid pairs is constructed as follows.
pairs = (1..mx/2).flat_map { |n| (1..mx/n).map { |m| [n, n*m] } }
#=> [[1, 1], [1, 2],..., [1, 50],
# [2, 2], [2, 4],..., [2, 50],
# [3, 3], [3, 6],..., [3, 48],
# [4, 4], [4, 8],..., [4, 48],
# [5, 5], [5, 10],..., [5, 50],
# [6, 6], [6, 12],..., [6, 48],
# [7, 7], [7, 14],..., [7, 49],
# [8, 8], [8, 16],..., [8, 48],
# [9, 9], [9, 18],..., [9, 45],
# [10, 10], [10, 20],..., [10, 50],
# [11, 11], [11, 22],..., [11, 44],
# [12, 12], [12, 24],..., [12, 48],
# [13, 13], [13, 26], [13, 39],
# [14, 14], [14, 28], [14, 42],
# [15, 15], [15, 30], [15, 45],
# [16, 16], [16, 32], [16, 48],
# [17, 17], [17, 34],
# [18, 18], [18, 36],
# [19, 19], [19, 38],
# [20, 20], [20, 40],
# [21, 21], [21, 42],
# [22, 22], [22, 44],
# [23, 23], [23, 46],
# [24, 24], [24, 48],
# [25, 25], [25, 50]]
pairs_size = pairs.size
#=> 182
Now you can draw random samples of, say, size
sample_size = 10
from this population, either with replacement:
sample_size.times.map { pairs[rand pairs_size] }
#=> [[3, 6], [2, 10], [7, 35], [2, 40], [18, 36],
# [1, 45], [11, 22], [2, 40], [1, 6], [9, 36]]
or without replacement:
pairs.sample(sample_size)
#=> [[22, 22], [6, 42], [1, 28], [1, 42], [1, 20],
# [1, 36], [23, 46], [9, 18], [4, 36], [16, 16]]
If arrays [n, n] (e.g, [2, 2]) are not to be included in pairs, change the block above to
{ |n| (2..mx/n).map { |m| [n, n*m] } }
in which case pairs.size #=> 157.

How can I combine elements of an array that have a common element?

I want to take an array of number pairs:
[[2, 3], [2, 15], [3, 15], [7, 8], [8, 7], [11, 15]]
and if two of these number pairs have a number in common, combine them and display the unique values, ultimately creating this array:
[[2, 3, 15, 11], [7, 8]]
Order of the numbers in the output array is not important. What would be the best solution?
The answer was corrected following Howard's comment.
[[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
.each_with_object([]) do |e1, a|
a.select{|e2| (e1 & e2).any?}.each{|e2| e1.concat(a.delete(e2))}
e1.uniq!
a.push(e1)
end
# => [[8, 7], [15, 11, 3, 2]]
It's really just another mapping and sorting problem,:
array = [[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
array.map{|a| array.each{|x| a |= x if (x & a).any?}; a.sort}.uniq
#=> [[2, 3, 11, 15], [7, 8]]
Not as elegant as #sawa's solution - but works for all the cases:
def includes? item, groups
groups.each{|sub|
return true if sub.include?(item)
}
false
end
def add groups, match, item
groups.each{|sub|
if sub.include?(match)
sub << item
sub.uniq!
return
end
}
end
def iterate arr
groups = []
grp = []
arr.each do |a,b|
grp = [a,b] if grp.empty?
if includes? a, groups
add groups, a, b
elsif includes? b, groups
add groups, b, a
else
groups << [a,b]
end
end
groups
end
arr = [[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
p iterate(arr)
OUTPUT
[[2, 3, 15, 11], [7, 8]]
A recursive solution:
def doit(a,b)
return a unless b
h = b.group_by {|e| (a[-1] & e).any?}
h[true] ? a[-1] |= h[true].flatten : a << h[false].pop
doit(a, h[false])
end
arr = [[2, 3], [2, 15], [3, 2], [3, 15], [16, 21], [7, 8], [8, 7], \
[21, 44], [11, 15], [15, 2], [15, 3], [15, 11], [8, 9]]
doit([arr.pop], arr) # => [[8, 9, 7], [15, 11, 2, 3], [21, 44, 16]]

Remove/squash entries in a vertical hash

I have a grid that represents an X, Y matrix, stored as a hash here.
Some points on the X Y matrix may have values (as type string), and some may not.
A typical grid could look like this:
{[9, 5]=>"Alaina", [10, 3]=>"Courtney", [11, 1]=>"Gladys", [8, 7]=>"Alford", [14, 11]=>"Lesley", [17, 2]=>"Lawson", [0, 5]=>"Katrine", [2, 1]=>"Tyra", [3, 3]=>"Fredy", [1, 7]=>"Magnus", [6, 9]=>"Nels", [7, 11]=>"Kylie", [11, 0]=>"Kellen", [10, 2]=>"Johan", [14, 10]=>"Justice", [0, 4]=>"Barton", [2, 0]=>"Charley", [3, 2]=>"Magnolia", [1, 6]=>"Maximo", [7, 10]=>"Olga", [19, 5]=>"Isadore", [16, 3]=>"Delfina", [17, 1]=>"Noe", [20, 11]=>"Francis", [10, 5]=>"Creola", [9, 3]=>"Bulah", [8, 1]=>"Lempi", [11, 7]=>"Raquel", [13, 11]=>"Jace", [1, 5]=>"Garth", [3, 1]=>"Ernest", [2, 3]=>"Malcolm", [0, 7]=>"Alejandrin", [7, 9]=>"Marina", [6, 11]=>"Otilia", [16, 2]=>"Hailey", [20, 10]=>"Brandt", [8, 0]=>"Madeline", [9, 2]=>"Leanne", [13, 10]=>"Jenifer", [1, 4]=>"Humberto", [3, 0]=>"Nicholaus", [2, 2]=>"Nadia", [0, 6]=>"Abigail", [6, 10]=>"Zola", [20, 5]=>"Clementina", [23, 3]=>"Alvah", [19, 11]=>"Wallace", [11, 5]=>"Tracey", [8, 3]=>"Hulda", [9, 1]=>"Jedidiah", [10, 7]=>"Annetta", [12, 11]=>"Nicole", [2, 5]=>"Alison", [0, 1]=>"Wilma", [1, 3]=>"Shana", [3, 7]=>"Judd", [4, 9]=>"Lucio", [5, 11]=>"Hardy", [19, 10]=>"Immanuel", [9, 0]=>"Uriel", [8, 2]=>"Milton", [12, 10]=>"Elody", [5, 10]=>"Alexanne", [1, 2]=>"Lauretta", [0, 0]=>"Louvenia", [2, 4]=>"Adelia", [21, 5]=>"Erling", [18, 11]=>"Corene", [22, 3]=>"Haskell", [11, 11]=>"Leta", [10, 9]=>"Terrence", [14, 1]=>"Giuseppe", [15, 3]=>"Silas", [12, 5]=>"Johnnie", [4, 11]=>"Aurelie", [5, 9]=>"Meggie", [2, 7]=>"Phoebe", [0, 3]=>"Sister", [1, 1]=>"Violet", [3, 5]=>"Lilian", [18, 10]=>"Eusebio", [11, 10]=>"Emma", [15, 2]=>"Theodore", [14, 0]=>"Cassidy", [4, 10]=>"Edmund", [2, 6]=>"Claire", [0, 2]=>"Madisen", [1, 0]=>"Kasey", [3, 4]=>"Elijah", [17, 11]=>"Susana", [20, 1]=>"Nicklaus", [21, 3]=>"Kelsie", [10, 11]=>"Garnett", [11, 9]=>"Emanuel", [15, 1]=>"Louvenia", [14, 3]=>"Otho", [13, 5]=>"Vincenza", [3, 11]=>"Tate", [2, 9]=>"Beau", [5, 7]=>"Jason", [6, 1]=>"Jayde", [7, 3]=>"Lamont", [4, 5]=>"Curt", [17, 10]=>"Mack", [21, 2]=>"Lilyan", [10, 10]=>"Ruthe", [14, 2]=>"Georgianna", [4, 4]=>"Nyasia", [6, 0]=>"Sadie", [16, 11]=>"Emil", [21, 1]=>"Melba", [20, 3]=>"Delia", [3, 10]=>"Rosalee", [2, 8]=>"Myrtle", [7, 2]=>"Rigoberto", [14, 5]=>"Jedidiah", [13, 3]=>"Flavie", [12, 1]=>"Evie", [8, 9]=>"Olaf", [9, 11]=>"Stan", [20, 2]=>"Judge", [5, 5]=>"Cassie", [7, 1]=>"Gracie", [6, 3]=>"Armando", [4, 7]=>"Delia", [3, 9]=>"Marley", [16, 10]=>"Robyn", [2, 11]=>"Richie", [12, 0]=>"Gilberto", [13, 2]=>"Dedrick", [9, 10]=>"Liam", [5, 4]=>"Jabari", [7, 0]=>"Enola", [6, 2]=>"Lela", [3, 8]=>"Jade", [2, 10]=>"Johnson", [15, 5]=>"Willow", [12, 3]=>"Fredrick", [13, 1]=>"Beau", [9, 9]=>"Carlie", [8, 11]=>"Daisha", [6, 5]=>"Declan", [4, 1]=>"Carolina", [5, 3]=>"Cruz", [7, 7]=>"Jaime", [0, 9]=>"Anthony", [1, 11]=>"Esta", [13, 0]=>"Shaina", [12, 2]=>"Alec", [8, 10]=>"Lora", [6, 4]=>"Emely", [4, 0]=>"Rodger", [5, 2]=>"Cedrick", [0, 8]=>"Collin", [1, 10]=>"Armani", [16, 5]=>"Brooks", [19, 3]=>"Eleanora", [18, 1]=>"Alva", [7, 5]=>"Melissa", [5, 1]=>"Tabitha", [4, 3]=>"Aniya", [6, 7]=>"Marc", [1, 9]=>"Marjorie", [0, 11]=>"Arvilla", [19, 2]=>"Adela", [7, 4]=>"Zakary", [5, 0]=>"Emely", [4, 2]=>"Alison", [1, 8]=>"Lorenz", [0, 10]=>"Lisandro", [17, 5]=>"Aylin", [18, 3]=>"Giles", [19, 1]=>"Kyleigh", [8, 5]=>"Mary", [11, 3]=>"Claire", [10, 1]=>"Avis", [9, 7]=>"Manuela", [15, 11]=>"Chesley", [18, 2]=>"Kristopher", [24, 3]=>"Zola", [8, 4]=>"Pietro", [10, 0]=>"Delores", [11, 2]=>"Timmy", [15, 10]=>"Khalil", [18, 5]=>"Trudie", [17, 3]=>"Rafael", [16, 1]=>"Anthony"}
What I need to do though, is basically remove all the empty entries.
Let's say [17,3] => Raphael does not have an element in front of if (let's say - no [16,3] exists) then [17,3] should become [16,3] etc.
So basically all empty items will be popped off the vertical (row) structure of the hash.
Are there functions I should have a look at or is there an easy squash-like method that would just remove blanks and adjust and move other items?
Thanks in advance for your help.
Here's my shot at it (probably not the fastest possible):
data.group_by{|k,v| k[1]}.inject({}){|a,(k,v)|
v.sort_by{|i| i[0][0]}.each_with_index{|elem,i|
a[[i,elem[0][1]]] = elem[1]
}
a
}
And here's input subset suitable for testing:
{[9, 5]=>"Alaina", [10, 3]=>"Courtney", [11, 5]=>"Gladys", [8, 5]=>"Alford"}
which should result with:
{[0, 5]=>"Alford", [1, 5]=>"Alaina", [2, 5]=>"Gladys", [0, 3]=>"Courtney"}
Step-by-step explanation (you can experiment in irb):
We need group-by because we should process each row separately, so first we group records by row, making a hash of arrays (let's call each of these arrays r)
data.group_by{|k,v| k[1]}
# => {5=>[[[9, 5], "Alaina"], [[11, 5], "Gladys"], [[8, 5], "Alford"]], 3=>[[[10, 3], "Courtney"]]}
inject is there so we can create output hash, we start with the empty one and we'll add "transformed" element at a time. Transformation is simply replacing the column indexes with "compacted" indexes, so the idea is to somehow map array [8, 9, 11] to [0, 1, 2] (when processing row 5). That's why we need to sort array r (let's call the sorted array rs):
r = [[[9, 5], "Alaina"], [[11, 5], "Gladys"], [[8, 5], "Alford"]]
rs = r.sort_by{|i| i[0][0]}
#=> [[[8, 5], "Alford"], [[9, 5], "Alaina"], [[11, 5], "Gladys"]]
And then assign each element in rs an index, starting from 0. We use each_with_index for that, so that in elem, i we get pairs such as
[[8, 5], "Alford"], 0
[[9, 5], "Alaina"], 1
[[11, 5], "Gladys"], 2
and now we have all needed numbers to fill in the resulting hash a:
a[[i,elem[0][1]]] = elem[1]
so
a[[0,5]] = 'Alford'
a[[1,5]] = 'Alaina'
etc.
We need to return a (accumulator) from a block for inject to work properly.
That should be it. :)
Ok... I think i get what you want it to do; Here is how I would do it:
data = # the hash in the format above
rows = [] # a new array
# then place each row in the entry of rows corresponding to it's y value at
# the index of it's x value
data.each_pair {|key, value| rows[key[1]] ||= []; rows[key[1]][key[0]] = value }
# compact the x values (if there is an adjacent lower x available move to it)
# this is what i believe you meant in your example
rows.map(&:compact!)
# clear out the old data
data = {}
# fill the hash back up using the new compacted data
rows.each_with_index { |xs, y| xs.each_with_index { |entry, x| data[[x,y]] = entry } }
tested it on your data (along with adding some more difficult x values) and it seems to work :)
Roja

Resources