Learning about hashes in Ruby - ruby

I'm trying to figure out how to print just the key in a Hash right now. I'm still new to this and I'm missing something on how to just print the key. My code so far:
shopping_list = {
'milk' => false,
'eggs' => false,
'jalapenos' => true
}
puts "Here is your shopping list!"
shopping_list.each do |key|
puts "- #{key}"
end
My output:
Here is your shopping list!
- ["milk", false]
- ["eggs", false]
- ["jalapenos", true]
I just want to value to print out like:
Here is your shopping list!
- milk
- eggs
Eventually, I would like to leave out the true shopping item, in this instance, it would already be purchased. I would like to print out what I still need to buy first.

You can get values you want by combining #reject, #keys and #each methods like below:
shopping_list.reject { |key, value| value }.keys.each do |key|
puts "- #{key}"
end
As Cary Swoveland mentioned, the code above was created temporary arrays. If you don't want to created those, you can use the code below:
shopping_list.each do |key, value|
puts "- #{key}" if value == false
end

Figured it out.
shopping_list = {
'milk' => false,
'eggs' => false,
'jalapenos' => true
}
puts "Here is your shopping list!"
for key in shopping_list.keys()
puts "- #{key}"
end
My output.
Here is your shopping list!
- milk
- eggs
- jalapenos

Related

ruby print hash values in iteration

This is happening and it seems weird to me.
The following code prints nothing but blank lines:
matz = { "First name" => "Yukihiro",
"Last name" => "Matsumoto",
"Age" => 47,
"Nationality" => "Japanese",
"Nickname" => "Matz"
}
matz.each do |k|
puts matz[k]
end
if I change that to
matz.each do |k|
puts k
puts matz[k]
end
works
also
matz.each do |k,v|
puts matz[k]
end
works
Anybody has got any explain please
In:
matz.each do |k|
puts matz[k]
end
each k will be an array that represents a key-value pair such as ["First name", "Yukihiro"]. Since none of these pairs is a key of the hash matz, puts matz[k] is the same as puts nil.

ruby case when statement on a hash

MAP_PERSONAL_DATA_WITH_TAG = {"Flat/Unit No." => "FLD_ADD_1Line1",
"Building No./Name" => "FLD_ADD_2Line2",
"Street" => "FLD_ADD_3Line3",
"Postcode" => "FLD_ADD_4Postcode",
"City/Town" => "FLD_ADD_5City",
"Region" => "FLD_ADD_6State",
"Suburb" => "FLD_ADD_Town"
}
data_hash = {"Street" => "s", "Suburb" => "sb", "abc" => "hdkhd"}
data_hash.each do |key, value|
case key
when <if key equal to the key of MAP_PERSONAL_DATA_WITH_TAG i.e MAP_PERSONAL_DATA_WITH_TAG.has_key?(key)>
puts 'something'
when "Mobile"
puts "something mobile"
else
puts 'something else'
end
end
Rather than writing like
when "Flat/Unit No.","Building No./Name","Street","Postcode","City/Town","Region","Suburb"
Is there any better way to write this?
Like you already wrote:
MAP_PERSONAL_DATA_WITH_TAG.has_key?(key)
Or just:
MAP_PERSONAL_DATA_WITH_TAG[key]
Or (would be slower):
MAP_PERSONAL_DATA_WITH_TAG.keys.include?(key)
I believe the MAP_PERSONAL_DATA_WITH_TAG.has_key?(key) will do the thing. And it is best way to do it with the information you've supplied. You have a data source MAP_PERSONAL_DATA_WITH_TAG and request hash - data_hash. You need to check each request to be in data source.
Also as I remember you can delete all puts in the switch statement and put puts before the case.
But there is one more way. Leaving a case-when statement out. So this may be more elegant.
data_hash.each do |key, value|
# Check if the key is Mobile (this is non-trivial key)
puts "something mobile" if key == "Mobile"
# Simply check if there is data in the hash for the key
# if yes - the result will be a string and will eveluate to true
# causing the first string to be printed out. But if there is no
# other such key in the hash - you will get nil that will be
# evaluated to false resulting in the second string to be returned
# from expression in curly braces and sequentally to be printed.
puts ( MAP_PERSONAL_DATA_WITH_TAG[key] ? 'something' : 'something else' )
end

Ruby(Hard Way, Ex48) matching array to hash then assigning values to struct?

I've looked at everything from the Ruby docs on struct, map, array & hash; but for whatever reason haven't grasped HOW to do this. I have two questions.
What's a good way to see if an array and a hash have a value in common?
From a hash, how could I find a value and "automatically" take it's key (or viceversa) and include them in a new instance of something else (e.g. Pair.new)?
I saw this SO response, but it wasn't much help...Learn Ruby Hard Way ex. 48
Also looked at this too, but it didn't work & Zed says to use map func...
Using Ruby, what is the most efficient way to check if any key in a hash matches any values within an Array
The exercise instructions can be found here.
Ruby The Hard Way EX.48 Instructions
MY CODE (try#2080)
class Lexicon
Pair = Struct.new(:token, :key)
def scan(stuff)
#words = stuff.split(" ")
return analyze
end
def analyze
hash = { :direction => "north", :direction => "south",
:direction => "east", :direction => "west", :verb => "go",
:verb => "stop"}
#words.map do |word|
if word == hash[:direction]
#i need something here that says if word matches a value in hash...
#assign matching key/value to a new instance of Pair
Pair.new(word)
else
*#puts to see if anything is sticking*
puts "Oh god its not working #{word}"
puts Pair[]
puts hash[:direction]
end
end
end
end
a = Lexicon.new()
a.scan("north mama jeffrey homie")
TERMINAL
$ ruby lexicon.rb
Oh god its not working north
#<struct Lexicon::Pair token=nil, key=nil>
west
Oh god its not working mama
#<struct Lexicon::Pair token=nil, key=nil>
west
Oh god its not working Jeffrey
#<struct Lexicon::Pair token=nil, key=nil>
west
Oh god its not working homie
#<struct Lexicon::Pair token=nil, key=nil>
west
MY CODE #2081 Same as above but,
hash.each do |k, v|
if v == #words
#i need something here that says if word matches a value in hash...
#assign matching key/value to a new instance of Pair
Pair.new(k,v)
else
puts "Oh god its not working"
puts Pair[]
puts hash[:direction]
puts #words
end
end
TERMINAL
Oh god its not working
#<struct Lexicon::Pair token=nil, key=nil>
...
...
You've got several issues here. Most importantly, your hash is not functional; hashes are built on key-value pairs with unique keys, so your hash is actually the same as the following:
hash = { :direction => "west", :verb => "stop"}
You would probably be better off swapping the key-value pairs in your hash as follows:
hash = { "north" => :direction, "south" => :direction,
"east" => :direction, "west" => :direction, "go" => :verb,
"stop" => :verb }
#words.map do |word|
hash.keys.include?(word) ? Pair.new(hash[word], word) : Pair.new(:error, word)
end
def have_common_value?(array, hash)
hash.values.any? { |v| array.include? v }
end
def get_pair_from_value(hash, value)
hash.find { |k,v| v == value }
end
A hash can only have one unique key per value, therefore, part of your problem is that the hash you generate above will only return one :direction and one :location.
This should help get you closer to what you're looking for:
class Lexicon
Pair = Struct.new(:token, :key)
def scan(stuff)
#words = stuff.split(" ")
return analyze
end
def analyze
hash = { :directions => { :north => "north", :south => "south", :east => "east", :west => "west" },
:actions => { :verb => "go", :verb => "stop" } }
#words.map do |word|
if hash[:directions].values.include?(word)
Pair.new(word)
else
puts "Oh god its not working #{word}"
puts Pair[]
puts hash[:directions]
end
end
end
end
a = Lexicon.new()
a.scan("north mama jeffrey homie")

Nicely formatting output to console, specifying number of tabs

I am generating a script that is outputting information to the console. The information is some kind of statistic with a value. So much like a hash.
So one value's name may be 8 characters long and another is 3. when I am looping through outputting the information with two \t some of the columns aren't aligned correctly.
So for example the output might be as such:
long value name 14
short 12
little 13
tiny 123421
long name again 912421
I want all the values lined up correctly. Right now I am doing this:
puts "#{value_name} - \t\t #{value}"
How could I say for long names, to only use one tab? Or is there another solution?
Provided you know the maximum length to be no more than 20 characters:
printf "%-20s %s\n", value_name, value
If you want to make it more dynamic, something like this should work nicely:
longest_key = data_hash.keys.max_by(&:length)
data_hash.each do |key, value|
printf "%-#{longest_key.length}s %s\n", key, value
end
There is usually a %10s kind of printf scheme that formats nicely.
However, I have not used ruby at all, so you need to check that.
Yes, there is printf with formatting.
The above example should right align in a space of 10 chars.
You can format based on your widest field in the column.
printf ([port, ]format, arg...)
Prints arguments formatted according to the format like sprintf. If the first argument is the instance of the IO or its subclass, print redirected to that object. the default is the value of $stdout.
String has a built-in ljust for exactly this:
x = {"foo"=>37, "something long"=>42, "between"=>99}
x.each { |k, v| puts "#{k.ljust(20)} #{v}" }
# Outputs:
# foo 37
# something long 42
# between 99
Or, if you want tabs, you can do a little math (assuming tab display width of 8) and write a short display function:
def tab_pad(label, tab_stop = 4)
label_tabs = label.length / 8
label.ljust(label.length + tab_stop - label_tabs, "\t")
end
x.each { |k, v| puts "#{tab_pad(k)}#{v}" }
# Outputs:
# foo 37
# something long 42
# between 99
There was few bugs in it before, but now you can use most of printf syntax with % operator:
1.9.3-p194 :025 > " %-20s %05d" % ['hello', 12]
=> " hello 00012"
Of course you can use precalculated width too:
1.9.3-p194 :030 > "%-#{width}s %05x" % ['hello', 12]
=> "hello 0000c"
I wrote a thing
Automatically detects column widths
Spaces with spaces
Array of arrays [[],[],...] or array of hashes [{},{},...]
Does not detect columns too wide for console window
lists = [
[ 123, "SDLKFJSLDKFJSLDKFJLSDKJF" ],
[ 123456, "ffff" ],
]
array_maxes
def array_maxes(lists)
lists.reduce([]) do |maxes, list|
list.each_with_index do |value, index|
maxes[index] = [(maxes[index] || 0), value.to_s.length].max
end
maxes
end
end
array_maxes(lists)
# => [6, 24]
puts_arrays_columns
def puts_arrays_columns(lists)
maxes = array_maxes(hashes)
lists.each do |list|
list.each_with_index do |value, index|
print " #{value.to_s.rjust(maxes[index])},"
end
puts
end
end
puts_arrays_columns(lists)
# Output:
# 123, SDLKFJSLDKFJSLDKFJLSDKJF,
# 123456, ffff,
and another thing
hashes = [
{ "id" => 123, "name" => "SDLKFJSLDKFJSLDKFJLSDKJF" },
{ "id" => 123456, "name" => "ffff" },
]
hash_maxes
def hash_maxes(hashes)
hashes.reduce({}) do |maxes, hash|
hash.keys.each do |key|
maxes[key] = [(maxes[key] || 0), key.to_s.length].max
maxes[key] = [(maxes[key] || 0), hash[key].to_s.length].max
end
maxes
end
end
hash_maxes(hashes)
# => {"id"=>6, "name"=>24}
puts_hashes_columns
def puts_hashes_columns(hashes)
maxes = hash_maxes(hashes)
return if hashes.empty?
# Headers
hashes.first.each do |key, value|
print " #{key.to_s.rjust(maxes[key])},"
end
puts
hashes.each do |hash|
hash.each do |key, value|
print " #{value.to_s.rjust(maxes[key])},"
end
puts
end
end
puts_hashes_columns(hashes)
# Output:
# id, name,
# 123, SDLKFJSLDKFJSLDKFJLSDKJF,
# 123456, ffff,
Edit: Fixes hash keys considered in the length.
hashes = [
{ id: 123, name: "DLKFJSDLKFJSLDKFJSDF", asdfasdf: :a },
{ id: 123456, name: "ffff", asdfasdf: :ab },
]
hash_maxes(hashes)
# => {:id=>6, :name=>20, :asdfasdf=>8}
Want to whitelist columns columns?
hashes.map{ |h| h.slice(:id, :name) }
# => [
# { id: 123, name: "DLKFJSDLKFJSLDKFJSDF" },
# { id: 123456, name: "ffff" },
#]
For future reference and people who look at this or find it... Use a gem. I suggest https://github.com/wbailey/command_line_reporter
You typically don't want to use tabs, you want to use spaces and essentially setup your "columns" your self or else you run into these types of problems.

hash methods argument values

Working on trying to understand the syntax for calling on different values of a hash.
For example lets say I am trying to delete 'pants' How do go about setting the argument for something like this:
products = {124 => ['shoes', 59.99], 352 => ['shirt', 19.99], 777 => ['pants', 19.87],
667 => ['jacket', 39.99], 898 => ['shoulder_holster', 22.78]}
While writing a menu driven program for this hash I'm including error checking before deleteing or adding a key this is what I have so far:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets.to_s # Get value for argument
while products.has_value?( d + syntax for right here???? )!= true do
puts "This turned out false because product does not exsist!"
d = gets.to_s
end
puts "Congrats your out of the loop"
products.delete(d + again syntax problems ???? )
puts products
end
How do I enter the syntax for the argument if I where to delete pants. Would it be ([d,:number]) I'm not having luck with any resources online with how to delete or add in this scenario. Any help or code example would be appreciated,
Matt
products.to_a.select {|a| a.last.first == 'pants' }
That will get you the record that matches 'pants'.
[[777, ["pants", 19.87]]]
So I think you'll want
while !products.to_a.select {|a| a.last.first == d }.empty?
on your loop then use Dafydd's line to delete the record.
It depends on whether the user is inputing the ID number or the name "pants". If the former:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets # Get value for argument
until products.has_key?(d.to_i)
puts "This turned out false because product does not exsist!"
d = gets
end
puts "Congrats your out of the loop"
products.delete(d.to_i)
puts products
end
If it's "pants", then this is how you want to do it:
if a == 3 # Loop delete a Product
puts "Delete a Product"
d = gets.strip # Need to strip because otherwise the newline will wreck it
until products.find {|key, val| val.first == d}
puts "This turned out false because product does not exsist!"
d = gets.strip
end
puts "Congrats your out of the loop"
products.delete_if {|key, val| val.first == d}
puts products
end
Writing a "delete named product from hash" method
There are shorter ways of doing it, but shooting for clarity I came up with this:
products = {124 => ['shoes', 59.99], 352 => ['shirt', 19.99], 777 => ['pants', 19.87],
667 => ['jacket', 39.99], 898 => ['shoulder_holster', 22.78]}
def wipeProduct(hash, nameToDelete)
hash.each do |i|
key = i[0]
productName = i[1].first
hash.delete(key) if productName==nameToDelete
end
end
puts products.inspect
wipeProduct(products,'pants')
puts products.inspect
wipeProduct(products,'shoulder_holster')
puts products.inspect
bash-3.2$ ruby prod.rb
{352=>["shirt", 19.99], 898=>["shoulder_holster", 22.78], 667=>["jacket", 39.99], 777=>["pants", 19.87], 124=>["shoes", 59.99]}
{352=>["shirt", 19.99], 898=>["shoulder_holster", 22.78], 667=>["jacket", 39.99], 124=>["shoes", 59.99]}
{352=>["shirt", 19.99], 667=>["jacket", 39.99], 124=>["shoes", 59.99]}
I don't know if it's possible for "pants" to occur in the hash in multiple places, but since I used "hash.each(...)", the method wipeProduct(hash, nameToDelete) will test every hash entry.
The input type bug and how to fix it
When you take input, you're assigning the string you captured to d. Here's the proof:
irb(main):010:0> d = gets.to_s
12
=> "12\n"
irb(main):011:0> d.class
=> String
You can convert that string to a Fixnum like this:
irb(main):012:0> d.to_i
=> 12
irb(main):013:0> d.to_i.class
=> Fixnum
All keys in the products hash are Fixnums. Here's the proof:
irb(main):014:0> products.keys.each {|i| puts i.class}
Fixnum
Fixnum
Fixnum
Fixnum
Fixnum
=> [352, 898, 667, 777, 124]
So you need to capture the value for the argument with this line:
d = gets.to_i # Get value for argument
The deletion part of the answer:
From products, you can delete the pants entry programmatically with this:
products.delete(777)
Running it gets you this:
irb(main):003:0> products.delete(777)
=> ["pants", 19.87]
Notice that you supply the key value (in this case 777) to .delete() and that it returns an array consisting of the key and value in that order respectively.
An alternative implementation
I'm not sure if it's safe to modify a hash in a block that's iterating over the key-value pairs in the hash. If it isn't, you can just save up all the keys to be deleted and delete them after iterating over the hash:
def wipeProduct(hash, nameToDelete)
keysToDelete = []
hash.each do |i|
key = i[0]
productName = i[1].first
keysToDelete << key if productName==nameToDelete
end
keysToDelete.each {|key| hash.delete(key) }
end
Here's the neater way to delete the "pants" entry:
def wipeProduct(hash, nameToDelete)
hash.reject!{|key,value| nameToDelete==value.first}
end
The reject! block gets to see each key-value pair, and when it returns true, the key-value supplied will be removed from the hash.
if a == 3 # Loop delete a Product
puts "Delete a Product by its key number"
d = gets
while products.has_key?(d)!= false do
puts "You have selected a key that is not currently in use"
d = gets
end
puts "You have deleted"
products.delete(d)
puts products
end
This is what I ended up doing had some trouble with the until loop so swapped for a while loop though becasue it wouldn't accept newly entered keys for some reason

Resources