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
Related
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.
Stuck on a Code Wars Challenge: Complete the solution so that it takes an array of keys and a default value and returns a hash with all keys set to the default value.
My answer results in a parse error:
def solution([:keys, :default_value])
return { :keys => " ", :default_value => " " }
end
Am I missing something to do with returning a hash key with all the keys set to the default value?
Do as below :
def solution(keys,default_val)
Hash[keys.product([default_val])]
end
solution([:key1,:key2],12) # => {:key1=>12, :key2=>12}
Read Array#product and Kernel#Hash.
I'd advise amending your solution to this:
def solution(keys, default_value)
hash = {}
keys.each do |key|
value = default_value.dup rescue default_value
hash[key] = value
end
hash
end
The dup is to work around the nasty case where default_value is a string and you then do e.g.:
hash[:foo] << 'bar'
… with your version, this would modify multiple values in place instead of a single one.
How can I handle a large number of conditions in a case statement?
...I'm about to write a case statement with about 125 when's.
This is along the lines of what I'm doing now, based on each when I add a node to a Nokogiri XML document, each when has two values that get set in the node, before setting the namespace:
case var
when :string
property_uom_node = Nokogiri::XML::Node.new "test_value", #ixml.doc
property_uom_node['att'] = "val"
property_uom_node.namespace = #ixml.doc.root.namespace_definitions.find{|ns| ns.prefix=="dt"}
property_uom_node
when :integer
#do something else
when :blue
...
#100 more when statements
...
end
I'm not looking for domain specific advice, just if there is a clean way to do this without ending up with a 300 line method.
This is what I ended up doing:
lookup = {:lup => ["1","2"], :wup => ["1","2"]}
case param
when lookup.has_key?(param)
property_uom_node = Nokogiri::XML::Node.new "#{lookup[param][0]}", #ixml.doc
property_uom_node['att'] = #{lookup[param][1]}
property_uom_node.namespace = #ixml.doc.root.namespace_definitions.find{|ns| ns.prefix=="dt"}
property_uom_node
end
Many case statements can, and many should, be replaced with other structures. Basically, the idea is to separate the policy -- what you want the code to do -- from the implementation -- how the code does it.
Suppose that your case statement is keyed on a symbol (that is, each of then when clauses is a constant symbol):
case foo
when :one
puts 1
when :two
puts 2
when :three
puts 3
else
puts 'more'
end
This can be replaced mostly with a data structure:
INTS = {:one => 1, :two => 2}
key = :one
puts INTS[key] # => 1
What if there are two different values, and not just one? Then make each value its own hash:
DOGS = {
:dog1 => {:name => 'Fido', :color => 'white},
:dog2 => {:name => 'Spot', :color => 'black spots'},
}
key = :dog2
dog = DOGS[key]
puts "#{dog[:name]}'s color is #{dog[:color]}"
# => "Spot's color is black spots"
It looks like the second case statement only has one case. A hash is a good way to do a lookup(many cases). You might try it like this:
if val = lookup[param]
property_uom_node = Nokogiri::XML::Node.new(val[0], #ixml.doc)
property_uom_node['att'] = val[1]
property_uom_node.namespace = #ixml.doc.root.namespace_definitions.find{ |ns| ns.prefix == "dt" }
property_uom_node # return the node
else
# not one of our cases
end
I frequently write something like this:
a_hash['x'] ? a_hash['x'] += ' some more text' : a_hash['x'] = 'first text'
There ought to be a better way to do this, but I can't find it.
There are two ways to create initial values with for a Hash.
One is to pass a single object in to Hash.new. This works well in many situations, especially if the object is a frozen value, but if the object has internal state, this may have unexpected side-effects. Since the same object is shared between all keys without an assigned value, modifying the internal state for one will show up in all.
a_hash = Hash.new "initial value"
a_hash['a'] #=> "initial value"
# op= methods don't modify internal state (usually), since they assign a new
# value for the key.
a_hash['b'] += ' owned by b' #=> "initial value owned by b"
# other methods, like #<< and #gsub modify the state of the string
a_hash['c'].gsub!(/initial/, "c's")
a_hash['d'] << " modified by d"
a_hash['e'] #=> "c's value modified by d"
Another initialization method is to pass Hash.new a block, which is invoked each time a value is requested for a key that has no value. This allows you to use a distinct value for each key.
another_hash = Hash.new { "new initial value" }
another_hash['a'] #=> "new initial value"
# op= methods still work as expected
another_hash['b'] += ' owned by b'
# however, if you don't assign the modified value, it's lost,
# since the hash rechecks the block every time an unassigned key's value is asked for
another_hash['c'] << " owned by c" #=> "new initial value owned by c"
another_hash['c'] #=> "new initial value"
The block is passed two arguments: the hash being asked for a value, and the key used. This gives you the option of assigning a value for that key, so that the same object will be presented each time a particular key is given.
yet_another_hash = Hash.new { |hash, key| hash[key] = "#{key}'s initial value" }
yet_another_hash['a'] #=> "a's initial value"
yet_another_hash['b'] #=> "b's initial value"
yet_another_hash['c'].gsub!('initial', 'awesome')
yet_another_hash['c'] #=> "c's awesome value"
yet_another_hash #=> { "a" => "a's initial value", "b" => "b's initial value", "c" => "c's awesome value" }
This last method is the one I most often use. It's also useful for caching the result of an expensive calculation.
You can specify the initial value when you create your hash:
a_hash = { 'x' => 'first text' }
// ...
a_hash['x'] << ' some more text'
Since you are using the hash to collect strings, I assume you simply want to make sure that you don't get an error when appending. Therefore, I'd make the hash default the empty string. Then you can append without error, no matter the hash key.
a_hash = Hash.new {|h,k| h[k]=""}
texts = ['first text', ' some more text']
texts.each do |text|
a_hash['x'] << text
end
puts a_hash['x'] #=> 'first text some more text'
The constructor of Hash, in its first argument, have a default value for the keys. This way
>> a_hash = Hash.new "first text"
=> {}
>> a_hash['a']
=> "first text"
>> a_hash['b'] += ", edit"
=> "first text, edit"
In case you do not want to mess with the default= value (existing hash), you can shorten your code by using fetch(key [, default]) as a look-up with a default value:
a_hash['x'] = a_hash.fetch('x', 'first_text') + ' some more text'
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