Ruby Equivalent for Python's For/Else

Ruby equivalent for Python's for / else

Hm, you could write it as a ternary:

@items.empty? ? 'Empty' : @items.each { |item| item }

You may want to do something more useful in your block though, since each is executed for its side effects and returns the original receiver.

Update as per your comment: I guess the closest you could get is something like

unless @items.empty?
@items.each { |item| p item }
else
'Empty'
end

Python's equivalent of Ruby's ||=

I think there is some confusion from the people who aren't really sure what the conditional assignment operator (||=) does, and also some misunderstanding about how variables are spawned in Ruby.

Everyone should read this article on the subject. A TLDR quote:

A common misconception is that a ||= b is equivalent to a = a || b, but it behaves like a || a = b

In a = a || b, a is set to something by the statement on every run, whereas with a || a = b, a is only set if a is logically false (i.e. if it's nil or false) because || is 'short circuiting'. That is, if the left hand side of the || comparison is true, there's no need to check the right hand side.

And another very important note:

...a variable assignment, even if not run, immediately summons that variable into being.

# Ruby
x = 10 if 2 == 5
puts x

Even though the first line won't be run, x will exist on the second line and no exception will be raised.

This means that Ruby will absolutely ensure that there is a variable container for a value to be placed into before any righthand conditionals take place. ||= doesn't assign if a is not defined, it assigns if a is falsy (again, false or nil - nil being the default nothingness value in Ruby), whilst guaranteeing a is defined.

What does this mean for Python?

Well, if a is defined, the following:

# Ruby
a ||= 10

is actually equivalent to:

# Python
if not a:
a = 10

while the following:

# Either language
a = a or 10

is close, but it always assigns a value, whereas the previous examples do not.

And if a is not defined the whole operation is closer to:

# Python
a = None
if not a:
a = 10

Because a very explicit example of what a ||= 10 does when a is not defined would be:

# Ruby
if not defined? a
a = nil
end

if not a
a = 10
end

At the end of the day, the ||= operator is not completely translatable to Python in any kind of 'Pythonic' way, because of how it relies on the underlying variable spawning in Ruby.

Coffeescript idiom equivalent to Python's for/else?

For this certain usage, you can use EcmaScript 6 .find function. Similar method exists in Underscore.js, if you want to be compatible with browsers not supporting EcmaScript 6.

result = lst.find cond
if result?
do_stuff_with result
else
do_other_stuff()

However, there is no direct replacement for for else loop from Python. In general case, you will need to declare a boolean to store the state.

equivalent of Python's with in Ruby

Ruby has syntactically lightweight support for literal anonymous procedures (called blocks in Ruby). Therefore, it doesn't need a new language feature for this.

So, what you normally do, is to write a method which takes a block of code, allocates the resource, executes the block of code in the context of that resource and then closes the resource.

Something like this:

def with(klass, *args)
yield r = klass.open(*args)
ensure
r.close
end

You could use it like this:

with File, 'temp.txt', 'w' do |f|
f.write 'hi'
raise 'spitespite'
end

However, this is a very procedural way to do this. Ruby is an object-oriented language, which means that the responsibility of properly executing a block of code in the context of a File should belong to the File class:

File.open 'temp.txt', 'w' do |f|
f.write 'hi'
raise 'spitespite'
end

This could be implemented something like this:

def File.open(*args)
f = new(*args)
return f unless block_given?
yield f
ensure
f.close if block_given?
end

This is a general pattern that is implemented by lots of classes in the Ruby core library, standard libraries and third-party libraries.


A more close correspondence to the generic Python context manager protocol would be:

def with(ctx)
yield ctx.setup
ensure
ctx.teardown
end

class File
def setup; self end
alias_method :teardown, :close
end

with File.open('temp.txt', 'w') do |f|
f.write 'hi'
raise 'spitespite'
end

Note that this is virtually indistinguishable from the Python example, but it didn't require the addition of new syntax to the language.

Equivalent of .presence in Python

In Python, you can achieve this by doing the following, assuming params is a dict:

state = params.get('state')
country = params.get('country')
region = 'US' if (state and country) else None

The method dict.get(key) will return the value associated to the key that has been passed. If no such key exists, it returns None.

If you need to replace the empty values with actual empty strings, you may do this instead:

state = params.get('state', '')
country = params.get('country', '')
region = 'US' if (state and country) else ''

Overall, the "Pythonic" way of doing this is to use a Form:

class Address(Model):
state = ...
country = ...
region = ...

AddressForm = modelform_factory(Address)

#inside view
def view(request):
if request.method == 'POST':
form = AddressForm(request.POST, request.FILES)

if form.is_valid():
address = form.save(commit=False)
address.region = 'US' if address.state and address.country
address.save()

By creating a custom AddressForm class you can do this processing automatically on the instance before saving it. This is the precise role of Form classes.

Searching through a list of objects in Ruby and returning result(s) or raising an error if not found

If you are doing complex things with each, then reading this documentation is the
first step.

Documentation for Enumerable

find is the obvious method that comes to mind, but there are others such as collect or map that might meet your needs as well. Two nested each loops is very un-Ruby like. The inner loop might look like this

matches = @obj_list.find { |obj| func_returning_hash(obj.some_method)[@key] == search }

raise "ERROR: No obj found matching search #{search}" if matches.nil?
results << matches.map { |obj| func_creating_result(obj) }

That's still fairly ugly and not very Ruby-like.

There are lots of ruby style checkers out there these days. Sandi-meter, reek, etc. Using these tools will help you a lot with grokking the ruby way. Coming from python they will seem quite picky and silly, but if you want to get the Ruby way they are very useful.

As an aside, this code screams for a refactoring. You are breaking almost all the basic rules for designing reusable objects. But that can wait...

Sandi Metz Rules for Object Design

From Ruby to Python - Is there an equivalent of try?

Dictionaries: dict.get

You can use dict.get:

d = {'foo' : 'bar'}

print(d.get('foo'))
'bar'

print(d.get('xxxxx'))
None

You can also pass a default parameter to get:

print(d.get('xxxxx', 'Invalid Key!'))
Invalid Key!

The default value is printed out when the key does not exist in the dictionary.



Lists: custom try-except block

Unfortunately, lists do not have a dict.get equivalent in their API, so you'll need to implement one yourself. Thankfully, you can extend the list class and override __getitem__ to do this cleanly.

class MyList(list):
def __getitem__(self, idx, default='oops'):
try:
return super().__getitem__(idx)
except IndexError:
return default

l = MyList([10, 20])

l[1]
# 20

l[3]
# 'oops'


What's the Ruby equivalent of Python's output[:-1]?

I don't want to get too nitpicky, but if you want to be more like Python's approach, rather than doing "StackOverflow"[0..-2] you can do "StackOverflow"[0...-1] for the same result.

In Ruby, a range with 3 dots excludes the right argument, where a range with two dots includes it. So, in the case of string slicing, the three dots is a bit more close to Python's syntax.



Related Topics



Leave a reply



Submit