caarlos0 caarlos0 - 7 months ago 13
Ruby Question

Erlang vs Ruby list comprehensions

I just started to learn Erlang, and really like their list comprehension syntax, for example:

Weather = [{toronto, rain}, {montreal, storms}, {london, fog}, {paris, sun}, {boston, fog}, {vancounver, snow}].
FoggyPlaces = [X || {X, fog} <- Weather].


In this case,
FoggyPlaces
will evaluate to "london" and "boston".

What's the best way to do this in Ruby?

For example, an Array like (very common, I believe):

weather = [{city: 'toronto', weather: :rain}, {city: 'montreal', weather: :storms}, {city: 'london', weather: :fog}, {city: 'paris', weather: :sun}, {city: 'boston', weather: :fog}, {city: 'vancounver', weather: :snow}]


The best I got 'til now is:

weather.collect {|w| w[:city] if w[:weather] == :fog }.compact


But in this case, I have to call
compact
to remove
nil
values, and the example itself is not that readable as Erlang.

And even more, in the Erlang example, both
city
and
weather
are atoms. I don't even know how to get something that makes sense and looks good like this in Ruby.

Answer

First off, your data structures aren't equivalent. The equivalent Ruby data structure to your Erlang example would be more like

weather = [[:toronto, :rain], [:montreal, :storms], [:london, :fog], 
            [:paris, :sun], [:boston, :fog], [:vancouver, :snow]]

Secondly, yes, Ruby doesn't have list comprehensions nor pattern matching. So, the example will probably be more complex. Your list comprehension first filters all foggy cities, then projects the name. Let's do the same in Ruby:

weather.select {|_, weather| weather == :fog }.map(&:first)
# => [:london, :boston]

However, Ruby is centered around objects, but you are using abstract data types. With a more object-oriented data abstraction, the code would probably look more like

weather.select(&:foggy?).map(&:city)

which isn't too bad, is it?