How to use 2 for loop in Ruby on rails? - ruby-on-rails

I am learning ruby on rails and i am trying to iterate my data from 2 loops. But didn't know how it can be possible.
Here is my array in controller:
#players = [1, 2, 3]
for(#i; #i < #players; #i++)
for(#j = #i+1; #j < #players; #j++)
puts #i "with" #j
end
end
I want [1,2], [1,3], [2,3] as a result

To get all combinations of size 2 (1st and 2nd, 1st and 3rd, 2nd and 3rd) can use Array#combination. With a block, it will yield each pair:
#players = [1, 2, 3]
#players.combination(2) do |i, j|
puts "#{i} with #{j}"
end
Output:
1 with 2
1 with 3
2 with 3
Without a block, combination returns an Enumerator:
#players.combination(2)
#=> #<Enumerator: ...>
To get an array of combinations, call its to_a method:
#players.combination(2).to_a
#=> [[1, 2], [1, 3], [2, 3]]

You use each to loop
You should read https://www.tutorialspoint.com/ruby/ruby_overview.htm#
#players.each do |player|
p player
end

Related

How to improve memory usage when generating a contiguous permutation

I have code that needs to generate a contiguous permutation:
(1..n).flat_map {|x| array.map {|y| (x..y) unless x > y } }.compact
Which outputs:
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5], [2], [2, 3], [2, 3, 4], [2, 3, 4, 5],
[3], [3, 4], [3, 4, 5], [4], [4, 5], [5]]
It works really well with low sizes of n, but when I have n = 100000 I run out of memory. Is there a way to improve this but keeping them contiguous?
I need to perform a reject! iterating over poisonous and allergic arrays:
array = (1..n)
permutations = array.flat_map {|x| array.map {|y| (x..y) unless x > y } }.compact
poisonous.each_with_index do |x, i|
permutations.reject! { |y| y.include?(x) && y.include?(allergic[i]) }
end
The problem:
poisonous = [3,4,6]
allergic = [5,6,7]
These numbers can't be together:
3 -> 4
4 -> 6
6 -> 7
combinations = [[1], [3,4], [4,5]]
So, [3,4] is not a valid combination.
I was able to solve the memory issue by using less variables as possible and doing calculations directly while generating the permutation, although processing time has increased a bit, it is using a lot less memory (dropped from 800mb to 32mb approximately). I'm open for suggestions to improve it even more.
counter = 0
(1..n).each {|x|
(1..n).each {|y|
counter += 1 if !(x > y) && !poisonous.each_with_index.select {|poison, i| (x..y).include?(poison) && (x..y).include?(allergic[i])}.any?
}
}
The following computes the number of "clean combinations". As no large arrays are produced it has modest memory requirements.
require 'set'
def clean_combos(n, poisonous, allergic)
arr = (1..n).to_a
bad_allergies = poisonous.zip(allergic).to_h
arr.sum do |m|
arr.combination(m).sum do |combo|
combo_set = combo.to_set
bad_allergies.any? do |poison, allergy|
combo_set.include?(poison) && combo_set.include?(allergy)
end ? 0 : 1
end
end
end
n = 10
poisonous = [3,4,6]
allergic = [5,6,7]
clean_combos(n, poisonous, allergic)
#=> 479
bad_allergies is found to equal {3=>5, 4=>6, 6=>7}.
This concludes that, for the array [1, 2,..., 10], there are 479 combinations of elements of size between 1 and 10 such that, for each combination, 3 and 5 are not both included, and neither are 4 and 5, and 6 and 7.
See Array#zip, Array#to_h, Array#sum, Array#combination, Hash#any? and Set#include?. Array#to_set is added when include 'set' is executed.
I've converted each combo to a set to speed lookups.
Some tweaks might improve efficiency, so experimentation may be called for. This may depend on the size of the array poisonous (and of allergic) relative to n.

How to swap variables inside of a nested array?

I have 2 arrays that I've zipped together and now I'm trying to swipe values at even positions.
So this is what I've tried:
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
c = a.zip(b)
# Now c is equal to: [[1, 111], [2, 222],[3, 333],[4, 444]]
c.map.with_index do |item, index|
a = item[0]
b = item[1]
if index%2 == 0
a, b = b, a
end
end
What I would like to have:
c = [[1, 111], [222,2], [3, 333],[444, 4]]
But it's still not working, is there a better solution ? Or how could I fix mine to make it work ?
EDIT:
I've realized that I could probably just use the ".reverse" method to swap the element. But I still can't manage to make it work.
Perhaps try:
c.map.with_index do |item, index|
index%2 != 0 ? item.reverse : item
end
=> [[1, 111], [222, 2], [3, 333], [444, 4]]
I would probably go with
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
a.zip(b).each_with_index do |item, idx|
item.reverse! if idx.odd?
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
zip as you did and reverse! just the items where the index is odd.
Other options include:
a.map.with_index(1) do |item,idx|
[item].insert(idx % 2, b[idx -1])
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we use with_index starting with 1 and then use the modulo method to determine if the item in b should be placed at index 0 or index 1.
Or
a.zip(b).tap {|c| c.each_slice(2) {|_,b| b.reverse!}}
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we zip a and b as your example did then we take the sub Arrays in groups of 2 and reverse the second Array using reverse! which will modify the Array in place.

How does Ruby's map method work in this case?

I got the mistake when I want to add doubled values to an array:
arr = [1,2,3]
def my_mistake(arr)
result = Array.new
arr.map { |element| result << element * 2 }
end
#=> [[2, 4, 6], [2, 4, 6], [2, 4, 6]]
def solution(arr)
arr.map { |element| element * 2 }
end
#=> [2,4,6]
However, come back to my mistake and the definition of map method in Ruby.
Invokes the given block once for each element of self. Creates a new array containing the values returned by the block.
I think my_mistake method has to return [[2], [2, 4], [2, 4, 6]] but it doesn't.
Everyone can explain this case for me ?
The resulting array will contain three occurrences of the same reference to the same array, as result is the result of the expression result << element * 2. So the result of the map is (kind of) [result, result, result]. These all point to the same content, which is the content of result at the end of the process ([2, 4, 6]).
What you expected would be achieved if you clone the array at each point, so that every resulting element would point to a different array, and each addition would not affect the previously computed arrays:
arr.map { |element| (result << element * 2).clone }
=> [[2], [2, 4], [2, 4, 6]]
.map returns the last evaluated expression, so no need for the result << part there. Here's something that worked for me:
def my_mistake(arr)
result = [] # '= []' is same like '= Array.new', look-up "literal constructors in Ruby"
new_arr = [] # same like new_arr = Array.new
until arr.empty?
new_arr << arr.shift # we add each element of arr, one by one, starting from the beginning
output << new_arr.map { |e| e * 2} # we calculate *2 for each element
end
return result
end
p my_mistake(arr) #=> [[2], [2, 4], [2, 4, 6]]
If you're nto sure how this works, try putting "p output" after the 6th line:
def my_mistake(arr)
output = []
new_arr = []
until arr.empty?
new_arr << arr.shift
output << new_arr.map { |e| e * 2}
p output
end
return output
end
my_mistake(arr)
The program will print:
[[2]]
[[2], [2, 4]]
[[2], [2, 4], [2, 4, 6]]

Why is the last array in my loop the only array to display in my view?

I have an array of arrays, and when I loop through the array the only one displayed is the last array.
Here is the code that loops through the arrays;
#events.each do |event|
def get_sig_class_id(sig_id)
IpsSignature.where('sig_id =?', sig_id).first.sig_class_id
end
sig_id = get_sig_class_id(event.signature)
event_class_data.push(sig_id)
#event_class_array = Array.new(event_class_data.group_by {|x| x}.map {|k,v| [k,v.count]})
#event_class_array.each do |x|
#event_class = x
end
end
If I display #event_class_array in my view I get this [[1, 54], [30, 1], [2, 1]]
If I display #event_class in the view I only get [2, 1]
I'm looking to get [1, 54], [30, 1], [2, 1] with #event_class (single arrays, not an array of arrays as in #event_class_array, and not just the last one).
My displaying in the view is simply to see what data I'm getting returned, this will eventually end up in a Highcharts pie chart.
Here are my views, nothing much to see here..
<%= #event_class_array %>
and
<%= #event_class %>
#event_class_array.each do |x|
#event_class = x
end
this code means that for each array, #event_class is rewrited with the x. So only the last one is stored there.
If you want to insert all arrays you should do
#event_class = []
#event_class_array.each do |x|
#event_class << x
end
#event_class_array.each do |x|
#event_class = x
end
You're overwriting #event_class every time that line fires. Change it to something like
#event_class_array.each do |x|
#event_class += x
end
And you should be good.

ruby/rails how to insert column into multidimensional array

I have an array arr = [[1,2],[3,4]] and a column col = [5,6]
Is there an easy way to get an output of [[1,2,5],[3,4,6]] without looping? Thanks
Yes, using Array#transpose as follows:
arr = [[1,2],[3,4]]
col = [5,6]
pp (arr.transpose << col).transpose # => [[1, 2, 5], [3, 4, 6]]

Resources