Ryan
Ryan

Reputation: 5682

Exclude option from collection.map in Ruby on Rails?

I have a line like this:

<%= f.input :state_id, :input_html => {:value => (policy_address.state.name rescue nil)}, :required => true, :collection => states.map {|s| [ s.name, s.id] }, :include_blank => 'Please select'%>

I want to exclude a value from the states.map collection. I thought that this would work but it doesn't:

<%= f.input :state_id, :input_html => {:value => (policy_address.state.name rescue nil)}, :required => true, :collection => states.map {|s| [ s.name, s.id] unless s.name == "excluded_state" }, :include_blank => 'Please select'%>

I put in unless s.name == "excluded_state, but, again, it's not working:

What am I doing wrong?

Upvotes: 14

Views: 14225

Answers (3)

dconman
dconman

Reputation: 90

Another alternative to the provided answers is to use each_with_item in place of map

states.each_with_item([]) { |out, s| out << [s.name, s.id] unless s.name == "excluded_state" }

It's kind of a halfway between each and reduce/inject. It takes an argument in, iterates over your enumerable providing the argument and the element, then returns the argument at the end. You can manipulate the argument in the loop (it wouldn't make much sense not to).

Upvotes: 0

Eureka
Eureka

Reputation: 6145

map doesn't allow to skip values. You have to reject unwanted elements first.

states.reject { |s| s.name == "excluded_state" }.map { |s| [s.name, s.id] }

Another (dirtier) solution is to return nil for excluded elements and use Array#compact on the resulting array to remove those nil elements:

states.map { |s| s.name == "excluded_state" ? nil : [s.name, s.id] }.compact

Upvotes: 31

mikej
mikej

Reputation: 66263

Eureka's answer is good but here is just a brief explanation to clarify what's going on.

map returns a new array with the results of running block once for every element in the array. When you write [s.name, s.id] unless s.name == "excluded_state" this causes the block to return nil when s.name == "excluded_state" i.e. the result would be something like

[["NY", 1], nil, ["CA", 2]]

So you could use reject to remove the unwanted state first, or you could just use compact1 to remove the nil entry on the result of your map as you originally wrote it.


  1. Array#compact returns a copy of the array with all nil elements removed.

Upvotes: 6

Related Questions