NoMethodError: undefined method `except' for Hash - ruby

While using except on Hash in Ruby,
d = {}
d["a"]=1234
d["b"]=34
d["c"]=3
d.except(:b,:c)
I am getting NoMethodError:
NoMethodError: undefined method `except' for {"a"=>1234, "b"=>34, "c"=>3}:Hash from (irb):6 from
/Users/niranjan/.rvm/rubies/ruby-1.9.3-p551/bin/irb:12:in `<main>'
What am I doing wrong?

except is a Rails method (ActiveSupport to be exact). Your code does not reproduce that error when executing in Rails console:
> d = {}
# => {}
> d["a"]=1234
# => 1234
> d["b"]=34
# => 34
> d["c"]=3
# => 3
> d.except(:b,:c)
# => {"a"=>1234, "b"=>34, "c"=>3}

There is no Hash#except. You can implement it as follows:
d.reject { |k, v| ["b", "c"].include? k }
# => {"a"=>1234}
Note that it is not a Hash with indifferent access; "b" is not the same thing as :b.

As they have said above, there is no method 'except' meaning you have not defined 'except' anywhere in this code. If you need a refresher on how to build Hashes, a good one is: http://www.tutorialspoint.com/ruby/ruby_hashes.htm

Hash#except will be in Ruby 3
h = { a: 1, b: 2, c: 3 }
p h.except(:a) #=> {:b=>2, :c=>3}
https://www.ruby-lang.org/en/news/2020/09/25/ruby-3-0-0-preview1-released/

Related

How do I copy contents into a hash without changing the memory address to which that hash points?

I'm using Ruby 2.4. I'm confused about the whole reference vs value thing when copying the contents of a hash. How do I copy the contents of one hash into another without changing the reference (memory address?) of the hash? Below is the example of the problem I'm having ....
2.4.0 :003 > def copy_hash(h)
2.4.0 :004?> new_hash = {"a" => 1}
2.4.0 :005?> h = new_hash
2.4.0 :006?> end
=> :copy_hash
2.4.0 :007 > h = {"b" => 2}
=> {"b"=>2}
2.4.0 :008 > copy_hash(h)
=> {"a"=>1}
2.4.0 :009 > h
=> {"b"=>2}
In the function, I'm assigning the parameter to a new hash ...
h = new_hash
But once the function returns the original hash is unchanged. What's the right way to change the hash in the function so that when it returns the value of the parameter is also changed? That is, if my hash started out as
{"b" => 2}
I'd like the value to be
{"a"=>1}
after I invoke the "copy_hash" function.
You can use Hash#replace to replace the contents of the hash:
def copy(h)
new_hash = { 'a' => 1 }
h.replace(new_hash)
end
h = { 'b' => 2 }
copy(h)
h == { 'a' => 1 } # => true

Chaining double-splats in Ruby [duplicate]

I noticed what I find to be a very surprising behavior with the ** (double-splat) operator in Ruby 2.1.1.
When key-value pairs are used before a **hash, the hash remains unmodified; however, when key-value pairs are only used after the **hash, the hash is permanently modified.
h = { b: 2 }
{ a: 1, **h } # => { a: 1, b: 2 }
h # => { b: 2 }
{ a: 1, **h, c: 3 } # => { a: 1, b: 2, c: 3 }
h # => { b: 2 }
{ **h, c: 3 } # => { b: 2, c: 3 }
h # => { b: 2, c: 3 }
For comparison, consider the behavior of the single-* operator on arrays:
a = [2]
[1, *a] # => [1, 2]
a # => [2]
[1, *a, 3] # => [1, 2, 3]
a # => [2]
[*a, 3] # => [2, 3]
a # => [2]
The array remains unchanged throughout.
Do we suppose the sometimes-destructive behavior of ** is intentional, or does it look more like a bug?
In either case, where is the documentation describing how the ** operator is meant to work?
I also asked this question in the Ruby Forum.
UPDATE
The bug is fixed in Ruby 2.1.3+.
The answers to the question seem to be:
It's probably a bug, rather than intentional.
The behavior of the ** operator is documented very briefly in the core library rdoc.
Thanks to the suggestions of several commenters, I've posted the bug to the Ruby trunk issue tracker.
UPDATE:
The bug was fixed in changeset r45724. The comment there was "keyword splat should be non-destructive," which makes this an authoritative answer.
I noticed the diff between 2.1.5 and 2.3.1
Example is an irb method and a way of calling it
$ irb
>> def foo(opts) opts end
=> :foo
>> foo a: 'a', ** {a: 'b'}
In 2.1.5 the following results in retaining value
=> {:a=>"a"}
In 2.3.1 the value is 'b'
(irb):2: warning: duplicated key at line 2 ignored: :a
=> {:a=>"b"}
I am not sure which it should be?
In 2.3.1 the hash provided as double splat overrides the same key of the first item in a list.

Double-splat operator destructively modifies hash – is this a Ruby bug?

I noticed what I find to be a very surprising behavior with the ** (double-splat) operator in Ruby 2.1.1.
When key-value pairs are used before a **hash, the hash remains unmodified; however, when key-value pairs are only used after the **hash, the hash is permanently modified.
h = { b: 2 }
{ a: 1, **h } # => { a: 1, b: 2 }
h # => { b: 2 }
{ a: 1, **h, c: 3 } # => { a: 1, b: 2, c: 3 }
h # => { b: 2 }
{ **h, c: 3 } # => { b: 2, c: 3 }
h # => { b: 2, c: 3 }
For comparison, consider the behavior of the single-* operator on arrays:
a = [2]
[1, *a] # => [1, 2]
a # => [2]
[1, *a, 3] # => [1, 2, 3]
a # => [2]
[*a, 3] # => [2, 3]
a # => [2]
The array remains unchanged throughout.
Do we suppose the sometimes-destructive behavior of ** is intentional, or does it look more like a bug?
In either case, where is the documentation describing how the ** operator is meant to work?
I also asked this question in the Ruby Forum.
UPDATE
The bug is fixed in Ruby 2.1.3+.
The answers to the question seem to be:
It's probably a bug, rather than intentional.
The behavior of the ** operator is documented very briefly in the core library rdoc.
Thanks to the suggestions of several commenters, I've posted the bug to the Ruby trunk issue tracker.
UPDATE:
The bug was fixed in changeset r45724. The comment there was "keyword splat should be non-destructive," which makes this an authoritative answer.
I noticed the diff between 2.1.5 and 2.3.1
Example is an irb method and a way of calling it
$ irb
>> def foo(opts) opts end
=> :foo
>> foo a: 'a', ** {a: 'b'}
In 2.1.5 the following results in retaining value
=> {:a=>"a"}
In 2.3.1 the value is 'b'
(irb):2: warning: duplicated key at line 2 ignored: :a
=> {:a=>"b"}
I am not sure which it should be?
In 2.3.1 the hash provided as double splat overrides the same key of the first item in a list.

Hash inspection in IRB?

I have a hash:
hash = { test: 'Test' }
If I am in an irb session and I enter hash, it outputs the content of the hash:
{
:test => 'Test'
}
What method is being invoked on the variable hash when I do that?
The method is Hash#inspect.
hash = { test: 'Test' }
# => {:test=>"Test"}
hash.inspect
# => "{:test=>\"Test\"}"
Object.inspect
The method is usually used for printing object structure.
IRB calls #inspect method on your expressions and prints its result.
When your hash contains a lot of data, it can be painful to read its content in one line.
I like to use y that print the hash in yaml.
h = {:a => 1, :b => 2}
y h
# ---
# :b: 2
# :a: 1
# => nil
IRB will call Hash#inspect.
hash.inspect

rails-settings: NoMethodError: undefined method `merge' for []:Array

I'm trying to implement the rails-settings gem (https://github.com/100hz/rails-settings) into my Rails 3 project using Ruby 1.8.7
Setting and retrieving the settings works perfectly, but I get an error if I try getting all settings of a specific user.
So, in the 'rails console' the following works:
user = User.find(123)
user.settings.color = :red
user.settings.color
But if I try to get all settings:
user.settings.all
I get:
NoMethodError: undefined method `merge' for []:Array
from /[...]/.rvm/gems/ruby-1.8.7-p334/bundler/gems/rails-settings-883114dfd933/lib/rails-settings/settings.rb:55:in `all'
from (irb):5
line 55 in the settings.rb:
#retrieve all settings as a hash (optionally starting with a given namespace)
def self.all(starting_with=nil)
options = starting_with ? { :conditions => "var LIKE '#{starting_with}%'"} : {}
vars = thing_scoped.find(:all, {:select => 'var, value'}.merge(options))
result = {}
vars.each do |record|
result[record.var] = record.value
end
# line 55 is below this one...
##defaults.select{ |k| k =~ /^#{starting_with}/ }.merge(result).with_indifferent_access
end
Whats the problem here? Or is this a ruby 1.8.7 vs. 1.9.2 thing?
That's a Ruby 1.8.7 vs. 1.9.2 thing
The Hash select method under ruby 1.8.7 will return an Array of Arrays.
Example:
{:a => 'a', :b => 'b', :c => 'c'}.select {|k, v| v > 'a'} #=> [[:b,'b'],[:c,'c']]
While the same thing running Ruby 1.9.2 will return:
{:a => 'a', :b => 'b', :c => 'c'}.select {|k, v| v > 'a'} #=> {:b => 'b',:c => 'c'}
You will need to post process the result and turn it into a hsah again or use something like inject.
Edit:
Here is a quick/ugly example of the inject
{:a => 'a', :b => 'b', :c => 'c'}.inject({}) {|r, e| e[1] > 'a' ? r.merge({e[0] => e[1]}) : r }
Semantically speaking:
collection.inject(container) { |container, element| select_condition ? container + element : container }
Edit 2: (Based on #CaleyWoods post)
Hash[*##defaults.select{ |k,v| k =~ /^#{starting_with}/ }.flatten].merge(result)
The |k, v| will prevent unnecessary warnings.
This looks like it's trying to do a merge on an Array which is not a method the Array class in Ruby. Merge is supported for Hash, it looks like your returned object is not the correct type. The author was definitely relying on a hash, in the next to last line 'with_indifferent_access' is called which is trying to allow you to select items from the hash with strings or symbols.
I can't examine the gem further right now and I wish I could provide a more helpful answer. If this hasn't been answered later i'll come back and help you out.
Not sure why the author is using double and single quotes in options and vars. He's also trying to populate the hash by hand instead of with inject. It's not awful by any means but I think there's room for improvement in the small bit of code you posted from the file.

Resources