Why do I get an unexpected result with a ternary condition? - ruby

Can anyone explain to me this result please:
(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo
=> false
I expected it to return:
=> "Foo"

(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo
is like:
(trad = {foo: "Foo", bar:"Bar"}).has_key? (:foo ? trad[:foo] : :foo)
:foo ? trad[:foo] : :foo is evaluated to "Foo" because :foo is treated as truth value.
(trad = {foo: "Foo", bar:"Bar"}).has_key? "Foo" yields false because there's no "Foo" key.
Use following (override precedence by surrounding parentheses) to get the expected result:
>> ((trad = {foo: "Foo", bar:"Bar"}).has_key? :foo) ? trad[:foo] : :foo
=> "Foo"
Hash#fetch(key, default) seems more appropriate:
>> {foo: "Foo", bar:"Bar"}.fetch(:foo, :foo)
=> "Foo"
>> {foo: "Foo", bar:"Bar"}.fetch(:baz, :baz)
=> :baz

For want of parenthesis the app was lost...
(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo # => false
(trad = {foo: "Foo", bar:"Bar"}).has_key?(:foo) ? trad[:foo] : :foo # => "Foo"
I'd be careful writing code like this:
(trad = {foo: "Foo", bar:"Bar"})
It's not idiomatic, so use:
trad = {foo: "Foo", bar:"Bar"}
trad.has_key?...
The reason is, in times of panic, like at 2:45 AM when your coding partner gets a call about a system outage because he's on call, and dives into the code, that assignment could be hard to find.
In a code review I'd suggest something like this over the other:
trad = {foo: "Foo", bar:"Bar"}
trad.has_key?(:foo) ? trad[:foo]
: :foo # => "Foo"
Note: This only works on Ruby 1.9+.
That all said, I'd highly recommend using fetch as recommended by #falsetru.

Related

Double splat on `nil`

My understanding is that a single splat on a non-array object calls to_a and then dissociates the elements apart. And since nil.to_a is defined to be [], the following conversion happens:
[:foo, *nil, :bar]
# => [:foo, *nil.to_a, :bar]
# => [:foo, *[], :bar]
# => [:foo, :bar]
By analogy, I thought that a double splat on a non-hash object calls to_h and then dissociates the key-value pairs apart. And since nil.to_h is defined to be {}, I expected the following conversion to happen:
{"foo" => 1, **nil, "bar" => 2}
# => {"foo" => 1, **nil.to_h, "bar" => 2}
# => {"foo" => 1, **{}, "bar" => 2}
# => {"foo" => 1, "bar" => 2}
But actually, it raises an error: no implicit conversion of nil into Hash. Why does it behave like that?
Edit I am not asking about the reasoning behind the design. I am asking where my thinking is wrong regarding double splat.
Well it's our human being super power to recognize patterns and predict things. However it's not always true. This is one example. Ruby is not consistent in splat and double splat. Your way of thinking is a good way to "remember" but it's not exactly the way Ruby works on splats.
See this bug report for more detail. In this bug report, Ruby's author Matz rather to remove the feature of being able to splat nil than add double splat to nil.
The reason *nil works is because the splat operator works on anything that responds to to_a, and nil.to_a returns []. The reason **nil doesn't work is that nil doesn't respond to to_hash, which is to_a's double-splat counterpart.
If you wanted this behavior, you could monkey-patch NilClass:
class NilClass
def to_hash
{}
end
end
{ "foo" => 1, **nil, "bar" => 2 }
# => { "foo" => 1, "bar" => 2 }

Getting rid of \r\ from an array imported from a .txt file

Input.txt :
foo
fooboo
boofoo
boo
main.rb :
foo = File.read("input.txt")
print Array(foo)
#=> ["foo\r\fooboo\r\boofoo\r\boo"]
This output is no good. I want all my foo's and friends to be like this:
#=> ["foo", "fooboo", "boofoo", "boo"]
What am I doing wrong here?
Do as below :
File.readlines('input.txt').map(&:strip)
# => ["foo", "fooboo", "boofoo", "boo"]
or
File.read('input.txt').split
# => ["foo", "fooboo", "boofoo", "boo"]

How does Mongoid know the difference between string values and symbol values?

Consider this example:
> x = User.first # or any persisted Mongoid::Document
=> #<User _id: 52014532a6356d1ac9000001, ...>
> x.set :foo, :bar
=> :bar
> x.set :foo2, 'bar'
=> "bar"
Note that "foo" and "foo2" are not declared in Ruby anywhere.
THEN, in a MongoDB shell:
> db.users.findOne({_id: ObjectId('52014532a6356d1ac9000001')})
{
"_id" : ObjectId("52014532a6356d1ac9000001"),
"foo" : "bar",
"foo2" : "bar",
...
}
BUT NOW, back in Ruby:
> x = User.find x.id; nil # to clear out any possibility of metadata on the instance
=> nil
> [x.read_attribute(:foo), x.read_attribute(:foo2)]
=> [:bar, "bar"]
How does it know?
Seens like BSON supports a symbol type for values, googling around
I found it:
https://github.com/mongodb/mongo-ruby-driver/wiki/FAQ#FrequentlyAskedQuestions-Ruby-IseethatBSONsupportsasymboltype.DoesthismeanthatIcanstoreRubysymbolsinMongoDB%3F

Parse arguments in string format into an array, but respect the nested commas in default params

I am writing a ruby method that takes a string like this
"foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }, bar, baz = \"('foo,bar,baz')\", &block"
and returns an array like this:
["foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }", "bar", "baz = \"('foo,bar,baz')\"", "&block"]
However, so far I am unable to split the string correctly, my best effort still breaks the string on internal hashes e.g.
"foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }, bar, "baz = \"('foo,bar,baz')\", &block".scan(/(?:[^,(]|\([^)]*\))+/)
Which produces:
["foo = { :foo => 'bar'", " :baz => \"{'foo' : 'bar'", " 'bar' : 'biff' }\" :bar => 'baz' }", " bar", " baz = \"('foo,bar,baz')\"", " &block"]
I think the regex i am using is close but i am not sure how to check for both parenthesis and curly brackets. Presently, the regex only searches for parentheses.
This is my current regex:
/(?:[^,(]|\([^)]*\))+/
Any help is greatly appreciated.
this does not suit you?
string.split(',')

Create hash using block (Ruby)

Can I create a Ruby Hash from a block?
Something like this (although this specifically isn't working):
foo = Hash.new do |f|
f[:apple] = "red"
f[:orange] = "orange"
f[:grape] = "purple"
end
In Ruby 1.9 (or with ActiveSupport loaded, e.g. in Rails), you can use Object#tap, e.g.:
foo = Hash.new.tap do |bar|
bar[:baz] = 'qux'
end
You can pass a block to Hash.new, but that serves to define default values:
foo = Hash.new { |hsh, key| hsh[key] = 'baz qux' }
foo[:bar] #=> 'baz qux'
For what it's worth, I am assuming that you have a larger purpose in mind with this block stuff. The syntax { :foo => 'bar', :baz => 'qux' } may be all you really need.
I cannot understand why
foo = {
:apple => "red",
:orange => "orange",
:grape => "purple"
}
is not working for you?
I wanted to post this as comment but i couldn't find the button, sorry
Passing a block to Hash.new specifies what happens when you ask for a non-existent key.
foo = Hash.new do |f|
f[:apple] = "red"
f[:orange] = "orange"
f[:grape] = "purple"
end
foo.inspect # => {}
foo[:nosuchvalue] # => "purple"
foo # => {:apple=>"red", :orange=>"orange", :grape=>"purple"}
As looking up a non-existent key will over-write any existing data for :apple, :orange and :grape, you don't want this to happen.
Here's the link to the Hash.new specification.
What's wrong with
foo = {
apple: 'red',
orange: 'orange',
grape: 'purple'
}
As others have mentioned, simple hash syntax may get you what you want.
# Standard hash
foo = {
:apple => "red",
:orange => "orange",
:grape => "purple"
}
But if you use the "tap" or Hash with a block method, you gain some extra flexibility if you need. What if we don't want to add an item to the apple location due to some condition? We can now do something like the following:
# Tap or Block way...
foo = {}.tap do |hsh|
hsh[:apple] = "red" if have_a_red_apple?
hsh[:orange] = "orange" if have_an_orange?
hsh[:grape] = "purple" if we_want_to_make_wine?
}

Resources