Ternary Operator and Arrays

Given:
ary = Array.new
some_hash = {‘a’ => 1, ‘b’ => 2, ‘c’ => 3}
another_array = [‘a’, ‘c’]

Then:
another_array.each do |x|
ary << some_hash.has_key?(key) ? ’ ’ : some_hash[key]
end

Expected:
ary should be [1, ’ ', 3]

Actual:
[true, false, true]

Question:
Why does that happen?

-Felix

Felix D. wrote:

Given:
ary = Array.new
some_hash = {‘a’ => 1, ‘b’ => 2, ‘c’ => 3}
another_array = [‘a’, ‘c’]

Then:
another_array.each do |x|
ary << some_hash.has_key?(key) ? ’ ’ : some_hash[key]
end

Expected:
ary should be [1, ’ ', 3]

Actual:
[true, false, true]

Question:
Why does that happen?

-Felix

It’s probably a precedence issue. Looks like << is binding tighter than
?: .

Anyway, you can just do ary = another_array.collect{|x| some_hash[x] ||
’ '}

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

On Thu, Nov 19, 2009 at 7:16 PM, Felix D. [email protected] wrote:

Expected:
ary should be [1, ’ ', 3]

Actual:
[true, false, true]

Question:
Why does that happen?

First of all, I assume you copy/pasted wrong, since I get

NameError: undefined local variable or method `key’ for main:Object

Assuming you want to query the hash for each value of the
another_array, you can do this:

irb(main):025:0> another_array.each do |x|
irb(main):026:1* ary << (some_hash.has_key?(x) ? ’ ’ : some_hash[x])
irb(main):027:1> end
=> [“a”, “c”]
irb(main):028:0> ary
=> [" ", " "]

Please note the parenthesis.
And I have the suspicion that you have the logic backwards, since you
return " " when the hash has the key, and go to the hash when it
doesn’t (which will always return nil or the default value). Maybe you
wanted this:

irb(main):033:0> ary = []
=> []
irb(main):034:0> another_array.each do |x|
irb(main):035:1* ary << (some_hash.has_key?(x) ? some_hash[x]: ’ ')
irb(main):036:1> end
=> [“a”, “c”]
irb(main):037:0> ary
=> [1, 3]

Another way of doing this, more idiomatic would be:

irb(main):038:0> ary = another_array.map {|x| some_hash.has_key?(x) ?
some_hash[x]: ’ '}
=> [1, 3]
irb(main):039:0> ary
=> [1, 3]

But anyway, you won’t get a three element array, since you are
iterating over another_array, which has only two entries. Can you
explain how you should obtain 3 elements in the array? Is it all
elements in the hash that have their key in the another_array? If so:

irb(main):040:0> ary = some_hash.map {|k,v| another_array.include?(k) ?
v : ’ '}
=> [1, " ", 3]

Hope this helps,

Jesus.

On Fri, 20 Nov 2009, Felix D. wrote:

Expected:
ary should be [1, ’ ', 3]

Actual:
[true, false, true]

Question:
Why does that happen?

You need parenthesis around the ternary operation.

The reason being is that << has a higher precedence than ?:, so it
evaluates just the some_hash.has_key?(key), giving a true or a false
value.

In general, while parenthesis are often optional in Ruby, if you’re not
sure how something works (or it’s not clear), parenthesis are your
friend.

Matt

On Thu, Nov 19, 2009 at 12:29 PM, Marnen Laibow-Koser
[email protected]wrote:

It’s probably a precedence issue. Looks like << is binding tighter than
?: .

Anyway, you can just do ary = another_array.collect{|x| some_hash[x] ||
’ '}

You have to be careful with this, though, because nil and false could be
valid values.

ary = Array.new
some_hash = { ‘a’ => 1 , ‘b’ => 2 , ‘c’ => nil , ‘d’ => false }
another_array = [ ‘a’ , ‘c’ , ‘d’ , ‘e’ ]

#if you want nil and false to be valid values, this doesn’t do what you
expect
another_array.collect{|x| some_hash[x] || ’ ’ } # => [1, " ", " ", " "]

#you can set the default to the string, then it will return that if the
key
doesn’t exist
some_hash.default = ’ ’
another_array.collect{ |x| some_hash[x] } # => [1, nil, false, " "]