Why is a hash literal called a hash literal in Ruby? - ruby

This is probably something you learn in programming 101.
Disclaimer: I have no formal programming training. I am self-taught.
For me, literal hash is like what this website suggests: a third editable hash called "corned beef hash".
In Ruby, you have two data types:
hash
hash literals
Why is one called a literal? Is it because you literally type out the associative array? The website above claims it is because the definition is inline. If so, why is the hash not also called literal when you can type it out like this:
states = Hash.new
states["CA"] = "California"
states["MA"] = "Massachusetts"
states["NY"] = "New York"
states["MA"].reverse #=> "sttesuhcassaM"

The data type is just one: Hash. Hash is a class. You can instantiate objects and use them
h = Hash.new
h.store("CA", "California")
h["MA"] = "Massachusetts"
A literal is just a shortcut which let you create objects of that class without explicitly use that class.
h = { "CA" => "California", "MA" => "Massachusetts" }
Same for Arrays
a = Array.new
a.push(1)
a << 2
Or, with array literal
a = [1, 2]

Your confusion stems from this misconception:
In Ruby, you have two data types:
hash
hash literals
Firstly, there are many more data structures in the Ruby core.
But secondly, there is no such thing as "literal hash". Hash literals refer to syntax sugar for defining hashes in place, aka literally.
# This is a hash literal
x = {foo: 42, bar: :baz}
# This is not a hash literal
x = Hash.new
x[:foo] = 42
x[:bar] = :baz
They are completely identical. The only difference is one is more convenient, while the other is more dynamic.

A literal is a fixed value.
It cannot be edited, unless you assign it to a variable and then modify that (although then of course you are not actually modifying the literal).
https://en.wikipedia.org/wiki/Literal_(computer_programming)
So you can assign a literal to a variable, compare a variable to a literal, compare two literals, but you cannot in general modify a literal directly.
Edit: Note that cases where a literal is modified turn out to be creating a new object, unlike the same operation performed on a variable.
2.2.5 :001 > "foo".upcase!
=> "FOO"
2.2.5 :002 > "foo".object_id
=> 2204993280
2.2.5 :003 > "foo".upcase!.object_id
=> 2204964760
2.2.5 :004 > x = "foo"
=> "foo"
2.2.5 :005 > x.object_id
=> 2204927520
2.2.5 :006 > x.upcase!.object_id
=> 2204927520
2.2.5 :007 >

Related

How to tell if hash's keys are all symbol or string?

Is there a way to quickly check if the hash as symbols or strings as keys?
TL;DR
A flat Hash with a single level of keys can easily be interrogated, but nested data structures can't. Additionally, a Hash key isn't limited to just String or Symbol keys. You can use almost any Ruby object as a Hash key, with a few caveats about what methods the objects must #respond_to? in order to function properly as a key.
Using #keys.all?
If your Hash isn't a nested data structure, then you could simply ask the Hash if all its top-level keys are Symbol objects:
p {foo: 1, bar: 2, baz: 3}.keys.all? Symbol
#=> true
Just replace Symbol with String if you want to check the opposite.
However, if you have nested data structures, then you have to iterate over each key and value to symbolize or stringify each key. You can do this with a gem like Hashie, or natively round-trip the Hash to and from JSON to coerce symbols into strings (the default) or perform other object conversions during the serialization or parse. For example, with Ruby 3.1.2:
require "json"
hash = {foo: {bar: 1, baz: 2 }}
hash = JSON.parse hash.to_json
hash
#=> {"foo"=>{"bar"=>1, "baz"=>2}}
will ensure that all your keys are converted to String objects, or raise an exception if they can't be sensibly converted to a String. You can also pass other options to force different behavior, including support for various built-in or custom JSON additions if you want. Round-tripping with YAML or Marshal are also possible, although these are generally less safe than standard JSON if you have tainted or user-provided objects in your Hash.
You can use #all? to check whether the hash's keys are all symbols, like so:
hash = { foo: 1, bar: 2, baz: 3 }
hash.all? { |key, _value| key.is_a?(Symbol) }
#=> true
You may write the following.
def key_type(h)
case h.each_key.map(&:class).uniq
when [String] then String
when [Symbol] then Symbol
else nil
end
end
key_type('cats'=>9, 'dogs'=>2) #=> String
key_type(:cats=>9, :dogs=>2) #=> Symbol
key_type('cats'=>9, :dogs=>2) #=> nil
key_type(:cats=>9, 'dogs'=>2) #=> nil
key_type(9=>'cats') #=> nil
key_type(9=>'cats', 2=>'dogs') #=> nil
key_type({}) #=> nil
For example, if
h = { 'cats'=>9, 'dogs'=>2 }
then
a = h.each_key.map(&:class)
#=> [String, String]
b = a.uniq
#=> [String]
so String is returned. By contrast, if
h = { 'cats'=>9, :dogs=>2 }
then
a = h.each_key.map(&:class)
#=> [String, Symbol]
b = a.uniq
#=> [String, Symbol]
so nil is returned.

Setting hash value (fractions) in ruby and sorting

I receive the following hash:
my_hash = {a:(1/20), b:(1/26)}
But when I see the hash I get the following:
irb(main):019:0> my_hash = {a:(1/20), b:(1/26)}
=> {:a=>0, :b=>0}
irb(main):020:0> my_hash
=> {:a=>0, :b=>0}
As you can see it convert to Integer (0)
How can I leave as Rational, or float so I can sort my_hash.sort_by {|key, value| value}?
The syntax for a Rational literal in Ruby is <numerator>/<denominator>r, e.g. 1/2r or 23/42r. What you have is just integer division: 1 divided by 20 is 0.
my_hash = { a: 1/20r, b: 1/26r }
#=> { :a => (1/20), :b => (1/26) }
It looks like you might be a Smalltalk or Scheme programmer, but in those languages the situation is different: they had rational literals from the beginning, Ruby only got them later, and so it needs an explicit annotation (the r suffix) to tell rational literals apart from just integer division; otherwise you would break existing programs.
Define as such:
my_hash = {a:(1.0/20.0), b:(1.0/26.0)}
Or alternatively:
my_hash = {a:(1.to_f/20.to_f), b:(1.to_f/26.to_f)}

Ruby Hash.new weirdness [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Strange ruby behavior when using Hash.new([])
This is a simple one, as I'm lost for words.
Why is this happening:
1.9.3-p194 :001 > h = Hash.new([])
=> {}
1.9.3-p194 :002 > h[:key1] << "Ruby"
=> ["Ruby"]
1.9.3-p194 :003 > h
=> {}
1.9.3-p194 :004 > h.keys
=> []
1.9.3-p194 :005 > h[:key1]
=> ["Ruby"]
When you create a hash like this:
h = Hash.new([])
it means, whenever the hash is accessed with a key that has not been defined yet, its going to return:
[]
Now when you do :
h[:key1] << "Ruby"
h[:key1] has returned [] , to which "Ruby" got pushed, resulting in ["Ruby"], as output, as that is the last object returned. That has also got set as the default value to return when 'h' is accessed with an undefined key.
Hence, when you do :
h[:key1] or h[:key2] or h[:whatever]
You will get
"Ruby"
as output.
Hope this helps.
Look at the documentation of Hash.new
new → new_hash
new(obj) → new_hash
new {|hash, key| block } → new_hash
If this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash.
In the first form, the access returns nil.
If obj is specified, this single object will be used for all default values.
If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.
irb(main):015:0> h[:abc] # ["Ruby"]
So ["Ruby"] is returned as default value instead of nil if key is not found.
This construction Hash.new([]) returns default value but this value is not initialized value of hash. You're trying to work with hash assuming that the default value is a part of hash.
What you need is construction which will initialize the hash at some key:
hash = Hash.new { |h,k| h[k] = [] }
hash[:key1] << "Ruby"
hash #=> {:key1=>["Ruby"]}
You actually did not set the value with h[:keys] << "Ruby". You just add a value for the returned default array of a not found key. So no key is created.
If you write this, it will be okay:
h = Hash.new([])
h[:keys1] = []
h[:keys1] << "Ruby"
I have to admit this tripped me out too when I read your question. I had a look at the docs and it became clear though.
If obj is specified, this single object will be used for all default values.
So what you actually doing is modifying this one single array object that is used for the default values, without ever assigning to the key!
Check it out:
h = Hash.new([])
h[:x] << 'x'
# => ['x']
h
# => {}
h[:y]
# => ['x'] # CRAZY TIMES
So you need to do assignment somehow - h[:x] += ['x'] might be the way to go.

Ruby value of a hash key?

I've got a list of values that are in a Ruby hash. Is there a way to check the value of the key and if it equals "X", then do "Y"?
I can test to see if the hash has a key using hash.has_key?, but now I need to know if hash.key == "X" then...?
Hashes are indexed using the square brackets ([]). Just as arrays. But instead of indexing with the numerical index, hashes are indexed using either the string literal you used for the key, or the symbol.
So if your hash is similar to
hash = { "key1" => "value1", "key2" => "value2" }
you can access the value with
hash["key1"]
or for
hash = { :key1 => "value1", :key2 => "value2"}
or the new format supported in Ruby 1.9
hash = { key1: "value1", key2: "value2" }
you can access the value with
hash[:key1]
This question seems to be ambiguous.
I'll try with my interpretation of the request.
def do_something(data)
puts "Found! #{data}"
end
a = { 'x' => 'test', 'y' => 'foo', 'z' => 'bar' }
a.each { |key,value| do_something(value) if key == 'x' }
This will loop over all the key,value pairs and do something only if the key is 'x'.
As an addition to e.g. #Intrepidd s answer, in certain situations you want to use fetch instead of []. For fetch not to throw an exception when the key is not found, pass it a default value.
puts "ok" if hash.fetch('key', nil) == 'X'
Reference: https://docs.ruby-lang.org/en/2.3.0/Hash.html .
How about this?
puts "ok" if hash_variable["key"] == "X"
You can access hash values with the [] operator
It seems that your question is maybe a bit ambiguous.
If “values” in the first sentence means any generic value (i.e. object, since everything in Ruby can be viewed as an object), then one of the other answers probably tells you what you need to know (i.e. use Hash#[] (e.g. hash[some_key]) to find the value associated with a key).
If, however, “values” in first sentence is taken to mean the value part of the “key, value pairs” (as are stored in hashes), then your question seems like it might be about working in the other direction (key for a given value).
You can find a key that leads to a certain value with Hash#key.
ruby-1.9.2-head :001 > hash = { :a => '1', :b => :two, :c => 3, 'bee' => :two }
=> {:a=>"1", :b=>:two, :c=>3, "bee"=>:two}
ruby-1.9.2-head :002 > a_value = :two
=> :two
ruby-1.9.2-head :003 > hash.key(a_value)
=> :b
If you are using a Ruby earlier than 1.9, you can use Hash#index.
When there are multiple keys with the desired value, the method will only return one of them. If you want all the keys with a given value, you may have to iterate a bit:
ruby-1.9.2-head :004 > hash[:b] == hash['bee']
=> true
ruby-1.9.2-head :005 > keys = hash.inject([]) do # all keys with value a_value
ruby-1.9.2-head :006 > |l,kv| kv[1] == a_value ? l << kv[0] : l
ruby-1.9.2-head :007?> end
=> [:b, "bee"]
Once you have a key (the keys) that lead to the value, you can compare them and act on them with if/unless/case expressions, custom methods that take blocks, et cetera. Just how you compare them depends on the kind of objects you are using for keys (people often use strings and symbols, but Ruby hashes can use any kind of object as keys (as long as they are not modified while they serve as keys)).
I didn't understand your problem clearly but I think this is what you're looking for(Based on my understanding)
person = {"name"=>"BillGates", "company_name"=>"Microsoft", "position"=>"Chairman"}
person.delete_if {|key, value| key == "name"} #doing something if the key == "something"
Output: {"company_name"=>"Microsoft", "position"=>"Chairman"}

Why isn't there a String#shift()?

I'm working my way through Project Euler, and ran into a slightly surprising omission: There is no String#shift, unshift, push, or pop. I had assumed a String was considered a "sequential" object like an Array, since they share the ability to be indexed and iterated through, and that this would include the ability to easily change the beginning and ends of the object.
I know there are ways to create the same effects, but is there a specific reason that String does not have these methods?
Strings don't act as an enumerable object as of 1.9, because it's considered too confusing to decide what it'd be a list of:
A list of characters / codepoints?
A list of bytes?
A list of lines?
Not being a Ruby contributor, I can't speak to their design goals, but from experience, I don't think that strings are regarded as 'sequential' objects; they're mutable in ways that suggest sequential behaviour, but most of the time they're treated atomically.
Case in point: in Ruby 1.9, String no longer mixes in Enumerable.
>> mystring = "abcdefgh"
=> "abcdefgh"
>> myarray = mystring.split("")
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
>> myarray.pop
=> "h"
>> mystring = myarray.join
=> "abcdefg"
this should do it, you wouldhave to convert it to an array, and then back though
UPDATE:
use String#chop! and Stirng#<<
>> s = "abc"
=> "abc"
>> s.chop!
=> "ab"
>> s
=> "ab"
>> s<<"def"
=> "abdef"
>> s
=> "abdef"
>>
Well at least in 1.9.2, you can deal with a string like an array.
ruby-1.9.2-p290 :001 > "awesome"[3..-1] => "some"
So if you want to do a sort of character left shift, just use [1..-1]
ruby-1.9.2-p290 :003 > "fooh!"[1..-1] => "ooh!"

Resources