How to use multiple .each statements in ruby? - ruby

I want to do something like this,
for topic.sections.each do |section| and topic.questions.each do |question|
print section
print question
end
I want both sections and questions simultaneously, the output will be like this:
section 1
question 1
section 2
question 2
I know what i did there is foolish, but, what is the exact way to do this or is this even possible?

Use Enumerable#zip.
For example,
sections = ['a', 'b', 'c']
questions = ['q1', 'q2', 'q3']
sections.zip(questions) { |section, question|
p [section, question]
}
# => ["a", "q1"]
# => ["b", "q2"]
# => ["c", "q3"]

Then do below with the help of Enumerable#zip:
topic.sections.zip(topic.questions) do |section,question|
p section
p question
end

Related

Set multiple keys to the same value at once for a Ruby hash

I'm trying to create this huge hash, where there are many keys but only a few values.
So far I have it like so...
du_factor = {
"A" => 1,
"B" => 1,
"C" => 1,
"D" => 2,
"E" => 2,
"F" => 2,
...etc., etc., etc., on and on and on for longer than you even want to know. What's a shorter and more elegant way of creating this hash without flipping its structure entirely?
Edit: Hey so, I realized there was a waaaay easier and more elegant way to do this than the answers given. Just declare an empty hash, then declare some arrays with the keys you want, then use a for statement to insert them into the array, like so:
du1 = ["A", "B", "C"]
du2 = ["D", "E", "F"]
dufactor = {}
for i in du1
dufactor[i] = 1
end
for i in du740
dufactor[i] = 2
end
...but the fact that nobody suggested that makes me, the extreme Ruby n00b, think that there must be a reason why I shouldn't do it this way. Performance issues?
Combining Ranges with a case block might be another option (depending on the problem you are trying to solve):
case foo
when ('A'..'C') then 1
when ('D'..'E') then 2
# ...
end
Especially if you focus on your source code's readability.
How about:
vals_to_keys = {
1 => [*'A'..'C'],
2 => [*'D'..'F'],
3 => [*'G'..'L'],
4 => ['dog', 'cat', 'pig'],
5 => [1,2,3,4]
}
vals_to_keys.each_with_object({}) { |(v,arr),h| arr.each { |k| h[k] = v } }
#=> {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2, "F"=>2, "G"=>3, "H"=>3, "I"=>3,
# "J"=>3, "K"=>3, "L"=>3, "dog"=>4, "cat"=>4, "pig"=>4, 1=>5, 2=>5, 3=>5, 4=>5}
What about something like this:
du_factor = Hash.new
["A", "B", "C"].each {|ltr| du_factor[ltr] = 1}
["D", "E", "F"].each {|ltr| du_factor[ltr] = 2}
# Result:
du_factor # => {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2, "F"=>2}
Create an empty hash, then for each group of keys that share a value, create an array literal containing the keys, and use the array's '.each' method to batch enter them into the hash. Basically the same thing you did above with for loops, but it gets it done in three lines.
keys = %w(A B C D E F)
values = [1, 1, 1, 2, 2, 2]
du_factor = Hash[*[keys, values].transpose.flatten]
If these will be more than 100, writing them down to a CSV file might be better.
keys = [%w(A B C), %w(D E F)]
values = [1,2]
values.map!.with_index{ |value, idx| Array(value) * keys[idx].size }.flatten!
keys.flatten!
du_factor = Hash[keys.zip(values)]
Notice here that I used destructive methods (methods ending with !). this is important for performance and memory usage optimization.

Ruby Parameters [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Could someone please explain lines 5 and 6? On line 5, is |word| a parameter? Why is it needed there? Also, on line 6, are {|a, b| b} also parameters. How should one read line 6? What is it doing?
puts "Input something: " # 1
text = gets.chomp # 2
words = text.split # 3
frequencies = Hash.new(0) # 4
words.each { |word| frequencies[word] += 1 } # 5
frequencies = frequencies.sort_by {|a, b| b} # 6
frequencies.reverse! # 7
On line 5, is |word| a parameter?
Yes, it's a block argument.
Why is it needed there?
From Array#each's documentation: "Calls the given block once for each element in self, passing that element as a parameter."
Example:
words = ["foo", "bar", "baz"]
words.each { |word| puts word }
The block is called three times. On the first pass, its block argument word is set to "foo", on the second pass it's set to "bar" and on the third pass it's set to "baz". Each time word is printed using puts.
Output:
foo
bar
baz
In your example, a hash is used to store the word frequencies. Within the each loop, the word's count is incremented.
How should one read line 6? What is it doing?
Enumerable#sort_by sorts a collection by the block's result. For example, to sort an array of strings by the string's length you would use:
["xxx", "xx", "x"].sort_by { |str| str.length }
#=> ["x", "xx", "xxx"]
Since frequencies is a hash, the block is called for each pair. Therefore, two arguments are set - a is the pair's key and b is the pair's value:
frequencies = { "foo" => 3, "bar" => 2, "baz" => 1}
frequencies = frequencies.sort_by { |a, b| b }
#=> [["baz", 1], ["bar", 2], ["foo", 3]]
It sorts the hash by its values. Note that sort_by returns an array. The array is assigned to the frequencies variable.
Instead of a and b you could use more descriptive argument names:
frequencies.sort_by { |word, count| count }

How might I match a string in ruby without using regular expressions?

Currently, I'm doing this:
(in initialize)
#all = Stuff.all.each.map {|t| t.reference_date }
#uniques = #all.uniq
results = []
#uniques.each do |k|
i = 0
#all.each do |x|
i += 1 if x =~ %r{#{x}}
end
results << [k, i]
end
And that's fine. It's going to work. But I like to avoid regular expressions when I can. I think they are a bit feo. That's spanish for ugly.
EDIT--
actually, that's not working because ruby "puts" the date as a numbered format like 2012-03-31 when the date object is placed inside of a string (as a variable, here), but its really a date object, so this worked:
if x.month == k.month && x.day == k.day
i += 1
end
You can do it with just 1 line (if I got right the question of course):
array = %w(a b c d a b d f t z z w w)
# => ["a", "b", "c", "d", "a", "b", "d", "f", "t", "z", "z", "w", "w"]
array.uniq.map{|i|[i, array.count(i)]}
# => [["a", 2], ["b", 2], ["c", 1], ["d", 2], ["f", 1], ["t", 1], ["z", 2], ["w", 2]]
results = Hash.new(0)
#all.each{|t| results[t] += 1}
# stop here if a hash is good enough.
# if you want a nested array:
results = results.to_a
This is the standard way of getting the frequency of elements in an enumerable.
Something you can do to avoid the appearance of regular expressions, is to build them on the fly using Regexp.union. The reason you might want to do this is SPEED. A well constructed regex is faster than iterating over a list, especially a big one. And, by allowing your code to build the regex, you don't have to maintain some ugly (feo) thing.
For instance, here's something I do in different chunks of code:
words = %w[peer_address peer_port ssl ssl_protocol ssl_key_exchange ssl_cipher]
regex = /\b(?:#{ Regexp.union(words).source })\b/i
=> /\b(?:peer_address|peer_port|ssl|ssl_protocol|ssl_key_exchange|ssl_cipher)\b/i
That makes it trivial to maintain a regex. And, try a benchmark using that to find substrings in text against iterating and it'll impress you.
If wildcards will work for you, try File.fnmatch
From your code I sense you want to get the number of occurrence of each reference_date. This can be achieved much easier by using ActiveRecord and SQL directly instead of pulling the whole tale and then performing time consuming operations in Ruby.
If you are using Rails 2.x you can use something like this:
Stuff.find(:all, :select => "reference_date, COUNT(*)", :group => "reference_date")
or if you are using Rails 3 then you can simplify it to
Stuff.count(:group => "reference_date")

Combining two arrays into a hash

I'm trying to combine two arrays into a hash.
#sample_array = ["one", "Two", "Three"]
#timesheet_id_array = ["96", "97", "98"]
I want to output the results into a hash called #hash_array. Is there a simple way to combine the two in a code block so that if you call puts at the end it looks like this in the console
{"one" => "96", "Two" => "97", "Three" => "98"}
I think this could be done in one or two lines of code.
try this
keys = [1, 2, 3]
values = ['a', 'b', 'c']
Hash[keys.zip(values)]
thanks
#hash_array = {}
#sample_array.each_with_index do |value, index|
#hash_array[value] = #timesheet_id_array[index]
end
Imho that looks best:
[:a,:b,:c].zip([1,2,3]).to_h
# {:a=>1, :b=>2, :c=>3}
Dr. Nic suggests 2 options explained well at http://drnicwilliams.com/2006/10/03/zip-vs-transpose/
#hash_array = {}
0.upto(#sample_array.length - 1) do |index|
#hash_array[#sample_array[index]] = #timesheet_id_array[index]
end
puts #hash_array.inspect

Interchanging hash keys and local variable names in Ruby [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a Ruby equivalent to PHP's extract?
Is there any way to take a Ruby hash such as:
myhash = {a: 12, b:24, c:36}
and convert this into a set of local variables (short of manually doing the assignments, obviously)? The result should be just as if I had executed the statements:
a = 12
b = 24
c = 36
Similarly, can I take a set of defined local variables and make a hash where the keys are the variable names and the values are the variable values (again, short of writing out the hash literally)? I haven't seen anything like this before so I'm guessing the answer is no, but it would be nice...
You probably know this already, but for the record: if you are willing to make instance variables instead of locals then this function will work:
def f x
x.each do |k, v|
instance_variable_set "##{k}", v
end
end
f :abc => 123, 'def' => 456
> #abc
=> 123
> #def
=> 456
You can do a lot of this stuff with Kernel.eval:
a = 1; b = 2; c = 3
local_variables.grep(/^[a-z]\w*/)
=> ["a", "b", "c"]
# Map local variables to hash
#h = Hash[local_variables.grep(/^[a-z]\w*/).map { |var| [var, eval(var)] }]
=> {"a"=>1, "b"=>2, "c"=>3}
# Map hash to local variables
#b = binding
#h.each { |k,v| eval("#{k}_new = #{v + 100}", #b) }
local_variables.grep(/^[a-z]\w*/)
=> ["a", "b", "c", "a_new", "b_new", "c_new"]
[a_new, b_new, c_new]
=> [101, 102, 103]

Resources