I have a code that iterates through two ranges. Please see the example below:
(0..6).each do |wday|
(0..23).each do |hour|
p [wday,hour]
end
end
Although this seems very concise and readable, sometimes 5 lines can be too much. One might want to write a more vertically compact code.
(0..6).to_a.product((0..23).to_a).each do |wday,hour|
p [wday, hour]
end
Above was my try, but the code looks very artificial to me. Am I missing something? Does ruby have a preferred way for this type of loop collapsing? If not, are there other alternatives to this workaround?
The following is slightly cleaner version of your loop:
[*0..6].product([*0..23]).each do |wday,hour|
p [wday, hour]
end
This approach does have the disadvantage of expanding the ranges into memory.
I think my preferred way of "collapsing" loops though, especially if the specific loop structure occurs in multiple places, is to turn the nested loops into a method that takes a block and yields to it. E.g.
def for_each_hour_in_week
(0..6).each do |wday|
(0..23).each do |hour|
yield wday,hour
end
end
end
for_each_hour_in_week do |wday,hour|
p [wday,hour]
end
This keeps the deep nesting out of the way of your logic, and makes your intent clear.
Related
Often when programming in Ruby, I find myself writing small for loops with a single statement in the body. For example...
for number in 1..10
puts number
end
In other languages like C, Java or Kotlin (for example), I'd be able to write the same code in two lines. For example...
// Kotlin
for (number in 1..10)
println(number)
In the above example, the ending of the loop body is inferred due to the lack of curly braces.
Does Ruby have a way to imitate this "single-statement" style of "for loop"?
Here are some of [my options/your potential replies], along with my thoughts on them.
You could append ; end to the body to end it on the same line.
This is true, and pretty sufficient, but I'd like to know if there's a more idiomatic approach.
This seems unnecessarily picky. Why would you ever want to do this?
You may think I'm being too picky. You may also think what I'm trying to do is un-idiomatic (if that's even a word). I totally understand, but I'd still love to know if it's do-able!
Doing this could let us write code that's even just a tiny bit nicer to read. And for programmers, readability matters.
Sure, you're looking for each, Range#each in this particular case:
(1..10).each { |number| puts number }
For more complex iterations use do - end block syntax. For example
(1..10).each do |number|
puts number
some_method_call(number)
Rails.logger.info("The #{number} is used")
something_else
end
To find more check out Ruby documentation, in particular, see Enumerable.
There is a vanishingly tiny number of cases, where any self-respecting Ruby programmer would even write an explicit loop at all. The number of cases where that loop is a for loop is exactly zero. There is no "more idiomatic" way to write a for loop, because for loops are non-idiomatic, period.
There is an even shorter syntax.
If you are just calling one method on each object you can use & syntax.
(1..3).collect(&:odd?) # => [true, false, true]
This is the same as
(1..3).collect { |each| each.odd? } # => [true, false, true]
This is the preferred way of writing loops in Ruby.
You'll quickly get used to both & and {} block syntax and the enumeration methods defined in Enumerable module. Some useful methods are
each which evaluates the block for each element
collect which create new array with the result from each block
detect which returns the first element for which block results true
select which create new array with elements for which block results true
inject which applies "folding" operation, eg sum = (1..10).inject { |a, b| a + b }
Fun fact, style guides for production code usually ban for loops at all because of a subtle but dangerous scoping issue. See more here, https://stackoverflow.com/a/41308451/24468
I came across three ways of writing a loop.
the_count = [1, 2, 3, 4, 5]
for loop 1
for number in the_count
puts "This is count #{number}"
end
for loop 2
the_count.each do |count|
puts "The counter is at: #{count}"
end
for loop 3
the_count.each {|i| puts "I got #{i}"}
Are there situations in which one way is a good practice or better solution than the other two? The first one is the most similar to the ones in other languages, and for me, the third one looks unorderly.
The first option is generally discouraged. It is possible in ruby to be more friendly towards developers coming from other languages (as they recognize the syntax) but it behaves a bit strange regarding variable visibility. Generally, you should avoid this variant everywhere and use only one of the block variants.
The advantage of the two other variants is that it works the same for all methods accepting a block, e.g. map, reduce, take_while and others.
The two bottom variants are mostly equivalent You use the each method and provide it with a block. The each method calls the block once for each element in the array.
Which one you use is mostly up to preference. Most people tend to use the one with braces for simple blocks which don't require a line-break. If you want to use a line-break in your block, e.g. if you have multiple statements there, you should use the do...end variant. This makes your code more readable.
There are other slightly more nuanced opinions on when you should use one or the other (e.g. some always use the braces form when writing functional block, i.e. ones which don't affect the outside of the block even when they are longer), but if you follow this above advice, you will please at least 98% of all ruby developers reading your code.
Thus, in conclusion, avoid the for i in ... variants (the same counts for while, until, ...) and always use the block-form. Use the do...end of block for complex blocks and the braces-form for simple one-line blocks.
When you use the the block form, you should however be aware of the slight differences in priority when chaining methods.
This
foo bar { |i| puts i }
is equivalent to
foo(bar{|i| puts i})
while
foo bar do |i|
puts i
end
is equivalent to
foo(bar) { |i| puts i }
As you can see, in the braces form, the block is passed to the right-most method while in the do...end form, the block is passed to the left-most method. You can always resolve the ambiguity with parenthesis though.
It should be noted that this is trade-off between idiomatic Ruby (solutions 2 and 3) and performant Ruby (using while loops, because for …in uses each under the hood) as pointed out in Yet Another Language Speed Test: Counting Primes:
Notably, it should be mentioned that writing idiomatic Python and Ruby results in much slower code than that used here. Ranges bad. While loops good.
While it's generally encouraged to opt for idiomatic Ruby, there are perfectly valid situations where you want to ignore that advice.
How can I rewrite the following code to be more Ruby-wayish? I'm thinking about inject but can't figure out how to do it.
def nested_page_path(page)
path = "/#{page.slug}"
while page.parent_id do
path.prepend "/#{page.parent.slug}"
page = page.parent
end
path
end
Input is an AR object, that has 0-5 consecutive parents. And output is something like '/pages/services/law'.
If you know for sure that there are no cycles in your parenting, you can do that recursively, i. e. with a function that calls itself. 5-level nesting should do just fine, trouble could arise with thousands.
def nested_page_path(page)
return "" if page.nil? # Or whatever that is root
"#{nested_page_path(page.parent)}/#{page.slug}"
end
But bear in mind, that the approach above, as well as yours, will fetch each object in a separate query. It's fine when you already have them fetched, but if not, you're in a bit of N+1 query trouble.
An easy workaround is caching. You can rebuild the nested path of this object and its descendants on before_save: that is some significant overhead on each write. There is a much better way.
By using nested sets you can get the object's hierarchy branch in just one query. Like this:
page.self_and_ancestors.pluck(:slug).join('/')
# ^
# Nested sets' goodness
What that query does is essentially "fetch me pages ordered by left bound, ranges of which enclose my own". I'm using awesome_nested_set in my examples.
SELECT "pages"."slug" FROM "pages"
WHERE ("pages"."lft" <= 42) AND ("pages"."rgt" >= 88)
ORDER BY "pages"."lft"
Without knowing your object structure it's difficult. But something recursive like this should do:
def nested_page_path(page)
path = "/#{page.slug}"
return path unless page.parent_id
path.prepend "#{nested_page_path(page.parent)}/"
end
Not sure inject is the simple answer since it operates on an Enumerable and you don’t have an obvious enumerable to start with.
I’d suggest something like this (not unlike your solution)
def nested_page_path(page)
pages = [page]
pages << pages.last.parent while pages.last.parent
'/' + pages.reverse.map(&:slug).join('/')
end
There’s scope for reducing repetition there, but that’s more or less what I’d go with.
I have following recursive function written in Ruby, however I find that the method is running too slowly. I am unsure if this the correct way to do it, so please suggest how to improve the performance of this code. The total file count including the subdirectories is 4,535,347
def start(directory)
Dir.foreach(directory) do |file|
next if file == '.' or file == '..'
full_file_path = "#{directory}/#{file}"
if File.directory?(full_file_path)
start(full_file_path)
elsif File.file?(full_file_path)
extract(full_file_path)
else
raise "Unexpected input type neither file nor folder"
end
end
With 4.5M directories, you might be better off working with a specialized lazy enumerator so as to only process entries you actually need, rather than generating each and every one of those 4.5M lists, returning the entire set and iterating through it in entirety.
Here's the example from the docs:
class Enumerator::Lazy
def filter_map
Lazy.new(self) do |yielder, *values|
result = yield *values
yielder << result if result
end
end
end
(1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
http://ruby-doc.org/core-2.1.1/Enumerator/Lazy.html
It's not a very good example, btw: the important part is Lazy.new() rather than the fact that Enumerator::Lazy gets monkey patched. Here's a much better example imho:
What's the best way to return an Enumerator::Lazy when your class doesn't define #each?
Further reading on the topic:
http://patshaughnessy.net/2013/4/3/ruby-2-0-works-hard-so-you-can-be-lazy
Another option you might want to consider is computing the list across multiple threads.
I don't think there's a way to speed up much your start method; it does the correct things of going through your files and processing them as soon as it encounters them. You can probably simplify it with a single Dir.glob do, but it will still be slow. I suspect that this is not were most of the time is spent.
There very well might be a way to speed up your extract method, impossible to know without the code.
The other way to speed this up might be to split the processing to multiple processes. Since reading & writing is probably what is slowing you down, this way would give you hope that the ruby code executes while another process is waiting for the IO.
I do not have problem as such but I am quite new to Ruby. I have the following 3 repeatable bits of code in one method and I would like to know how a true Rubyist would first of all remove the duplication and secondly make it more reusable.
Here is the code in question:
file = File.new( destination)
doc = REXML::Document.new file
doc.elements.each("configuration/continuity2/plans") do |element|
element.attributes["storebasedir"] = "#{TEST_OUTPUT_DIRECTORY}"
end
doc.elements.each("configuration/add").each do |database|
database.raw_attributes = database.attributes.merge("connectionstring" => "#{TEST_CONNECTION_STRING}")
end
doc.elements.each("configuration/connectionStrings/plans") do |connectionString|
connectionString.raw_attributes = connectionString.attributes.merge("connectionString" => "#{TEST_CONNECTION_STRING}")
end
Any advice appreciated.
The last two blocks could be replaced with
["add", "connectionStrings/plans"].each do |elt_name|
doc.elements.each("configuration/#{elt_name}").do |elt|
elt.raw_attributes = elt.attributes.merge("connectionString" => "#{TEST_CONNECTION_STRING}")
end
end
I assume the case difference between "connectionstring" and "connectionString" was accidental. If so, that exactly illustrates the benefit of removing duplication.
Also, you can probably replace "#{TEST_CONNECTION_STRING}" with TEST_CONNECTION_STRING.
You could try to add a method that tries to be as generic as possible to avoid this, but they are significantly different for me... You risk having complex code just to be able to wrap these lines into a single method.
I don't see the duplication. The collections you are iterating over and substantially different and the operation you are performing on each element is also very different. I agree with Olivier that any attempt at removing the duplication will simply result in more complex code.