I would like to create a hash with several members added conditionally.
The simple approach is:
var a = {}
a['b'] = 5 if condition_b
a['c'] = 5 if condition_c
a['d'] = 5 if condition_d
Now, I would like to write a more idiomatic code. I am trying:
a = {
b => (condition_b? 5 : null),
c => (condition_c? 5 : null),
d => (condition_d? 5 : null)
}
But now, a.length equals 3 whatever conditions are met. This is not the desired result.
Is there a handy solution?
May be not exactly what you want but this can help
array = ['b','c','d'] ##array of values you want as keys
a ={}
array.each do |val|
a[val] = 5 if send("condition_"+val.to_s) #call method condition_#{key_name}
end
If the conditions are not related you can use your own hash and you can
a = {
b => (condition_b? 5 : nil),
c => (condition_c? 5 : nil),
d => (condition_d? 5 : nil)
}
a.values.compact.size
to get length of values other then nil
How about you only add to the hash if the condition is met, like this:
a = {}
a.merge!({'b'=>5}) if condition_b
a.merge!({'c'=>5}) if condition_c
In the second way, you're always going to have the three keys; the conditions only determine the particular values. So if you want to use that syntax, you will need to define a custom method that only counts the keys if they are not nil (also, it's nil in Ruby, not null). Something like:
def non_nil_length(hash)
i = 0
hash.each_pair do |k,v|
if !v.nil?
i += 1
end
end
i
end
There's probably a better way to do that, but there's the quick and dirty.
Related
I'm trying to dynamically generate a case statement based on an array of values. For example let's say I have an array of ranges
[1..3,4..6,7..20,21..38]
and I want to write a dynamic case statement that returns the first number of whatever range
case n
ranges.each do |r|
when r
r.first
end
end
Is this possible, or will I have to find another way to do it (my actual code is more complex)?
If i get your question right, then you can forget case statement and do it using detect:
ary = [1..3, 4..6, 7..20, 21..38]
num = 15 # say
ary.detect { |sub_ary| sub_ary.include?(num) }
=> 7..20
ary.detect { |sub_ary| sub_ary.include?(num) }.first # call `first` on result of above, which is a range, to get the first element.
=> 7
Just out of curiosity:
number = 5
instance_eval [
"case number",
*ranges.map { |r| "when #{r} then (#{r}).first" },
"end"
].join($/)
#⇒ 4
In addition to #detect (or #find) with #include? from Jagdeep Singhs answer you can also use the case equality operator (Range#===). This operator is used by the case statement to compare the input value with the scenario's you're providing.
ranges.find { |range| range === n }.first
Keep in mind both #detect and #find return nil if no value can be found. This means you might want to use the safe navigation operator (}&.first) to prevent a no method exception of #first on nil if the value can't be found.
Well, this works, but is kind of pointless and thread unsafe:
def get_range(n)
ranges = [1..3,4..6,7..20,21..38]
case n
when 3
# special case
199
when ->(x) { #_get_range = ranges.find { |r| r.cover?(x) } }
#_get_range.first
else
0
end
ensure
remove_instance_variable(:#_get_range) if instance_variable_defined?(:#_get_range)
end
get_range(3) # => 199
get_range(5) # => 4
get_range(50) # => 0
You could just do:
ranges.find { |r| r.cover?(n) }&.first || 0
My two cents..
ranges = [1..3,4..6,7..20,21..38]
num = 15
ranges.bsearch { |range| range.member? num }.begin
Is there a method.send equivalent for proc?
eg:
def s a
a + 1
end
b = "s"
send b.to_sym,10 #=> 11
Is there something like this?
p = Proc.new{|s| s + 1}
d = "p"
*call d.to_sym,10 *
EDIT:
In response to mudasobwa's answer
I need to call the Procs from an array of methods.
eg:
ss = ["a","b","c","d"]
Is it possible in this case?
The other answers cover the exact question asked. But I say, it was a wrong approach. Don't do that runtime introspection. It brings no benefit. If you want to address your procs by name, put them in a hash and use "civilian-grade" ruby.
handlers = {
'process_payment' => proc { ... },
'do_this' => proc { ... },
'or_maybe_that' => proc { ... },
}
pipeline = ['process_payment', 'or_maybe_that']
pipeline.each do |method_name|
handlers[method_name].call
end
For this particular example:
p = Proc.new{|s| s + 1}
d = "p"
#⇒ *call d.to_sym,10 *
It would be:
binding.local_variable_get(d).(10)
#⇒ 11
Updated
Procs are objects, so you can store them in variables, arrays, hashs, just like any objects, and call them from those rather than by names.
If you need to make an array of procs, store the procs themself in an array, rather than the names of the variables you assigned them to. This way, you can pass this array around and call them all.
myprocs = []
myprocs << = Proc.new{|s| s + 1}
myprocs.each {|p| p.call(10)}
If you want to call them by names, use a hash.
myprocs = {}
myprocs["d"] = Proc.new{|s| s + 1}
myprocs["d"].call(10)
Using eval - bad practice, but as one of the possible solutions is:
p = Proc.new{|s| s + 1}
d = "p"
eval("#{d}[10]")
#=> 11
I have an array:
array = ['Footballs','Baseball','football','Soccer']
and I need to count the number of times Football or Baseball is seen, regardless of case and pluralization.
This is what I tried to do, but with no luck:
array.count { |x| x.downcase.include? 'football' || x.downcase.include? 'baseball' }
What is a right or better way to write this code? I am looking for 3 as an answer.
I would use count combined with a block that checks each element against a regular expression that matches the constraints you're looking for. In this case:
array.count { |element| element.match(/(football|baseball)s?\Z/i) }
This will match any of these elements: football, footballs, baseball, baseballs.
The s? makes the 's' optional, the i option (/i) makes the expression case insensitive, and the \Z option checks for the end of the string.
You can read more about Regexps in the Ruby docs: http://www.ruby-doc.org/core-2.0.0/Regexp.html
A great tool for playing with Regexps is Rubular: http://rubular.com/
If you give a block to the count method of array, it iterates over the array and counts the values for which you return true:
array.count do |x|
(x.downcase.include? 'footbal') || (x.downcase.include? 'baseball')
end
You can use inject to count each item and return the result.
array = ['Football','Baseball','football','Soccer']
count = array.inject({}) do |counter, item|
counter[item.downcase] ||= 0
counter[item.downcase] += 1
counter
end
# => {"football"=>2, "baseball"=>1, "soccer"=>1}
If you need to count a single value, it's even simpler.
array = ['Football','Baseball','football','Soccer']
count = array.inject(0) do |counter, item|
counter += (item.downcase == 'football' ? 1 : 0)
end
On one line
array = ['Football','Baseball','football','Soccer']
count = array.inject(0) { |counter, item| counter += (item.downcase == 'football' ? 1 : 0) }
To include pluralization, simply enhance the comparison.
Assuming you have Ruby on Rails installed for the singularize method (you don't actually need to run this in rails):
require 'active_support/inflector'
array = ['Footballs','Baseball','football','Soccer']
uniq = array.map { |s| s.downcase.singularize }.uniq
uniq.size # => 3
Using Rails and Ruby >= 2.7 you can do:
array = ['Footballs','Baseball','football','Soccer']
array.map(&:downcase).map(&:singularize).tally
=> {"football"=>2, "baseball"=>1, "soccer"=>1}
I'm getting to grips with rails and whilst I feel I am progressing there is one thing that I am struggling to get to grips with and it's very basic. I am trying to understand the different usage of [] {} and () Are there any good sources of their usage and are there any tips you can give to a beginner in recognizing when to use one or the other, or as I seem to see in some cases when they are not required at all?
I know this is extremely basic but I have struggled to find literature which explains concisely the interplay between them and Ruby or specifically RoR
It has nothing to do with RoR; the various brackets are Ruby language constructs.
[] is the array operator, for arrays and other classes that implement it (like a string taking a range to get substrings, or hashes to look up a key's value):
a = [1, 2, 3]
a.each { |n| puts n }
s = "ohai"
puts s[1..-1]
h = { foo: "bar", baz: "plugh" }
puts h[:foo]
{} is for hashes, and one of two ways of delimiting blocks (the other being begin/end). (And used with # for string interpolation.)
h = { foo: "bar", baz: "plugh" }
h.each { |k, v| puts "#{k} == #{v}" }
() is for method parameters, or for enforcing evaluation order in an expression.
> puts 5 * 3 + 5 # Normal precedence has * ahead of +
=> 20
> puts 5 * (3 + 5) # Force 3+5 to be evaluated first
=> 40
def foo(s)
puts(s)
end
They're sometimes optional if the statement has no ambiguity:
def foo s
puts s
end
(They're not always optional, and putting a space between the method call and its parenthetical parameter list can cause issues--best not to, IMO.)
(I probably missed something, too, but there's the nutshell.)
[] are used to access objects within a hash (via a key) or within an array (via an index).
hash[:key] # returns a value
array[0] # returns the first array element
[] is used to describe an array.
array = ['a', 'b', 'c']
Of course this can be nested.
nested = [['a','b','c'], [1,2,3]]
[] can be used to declare a hash, but that's because the Hash class can accept an array.
hash = Hash[['a',1], ['b',2]] # { 'a' => 1, 'b', => 2 }
{} is used to declare a hash.
hash = { 'a' => 1, 'b' => 2 }
This too can be nested.
hash = { 'a' => { 'c' => 3 }, 'b' => { 'd' => 4 } }
{} is also used to delimit blocks. The .each method is a common one. The following two blocks of code are equivalent.
array.each do |n|
puts n
end
array.each { |n| puts n }
The () is just used for grouping in cases where ambiguity needs clarification. This is especially true in methods that take many arguments, some of which may be nil, some of which may be obejcts, etc. You'll see a lot of code that omit them entirely as no grouping is needed for clarity.
puts(string)
puts string
I recommend firing up the rails console and start declaring variables and accessing them.
I've got a Ruby method like the following:
# Retrieve all fruits from basket that are of the specified kind.
def fruits_of_kind(kind)
basket.select { |f| f.fruit_type == kind.to_s }
end
Right now, you can call this like:
fruits_of_kind(:apple) # => all apples in basket
fruits_of_kind('banana') # => all bananas in basket
and so on.
How do I change the method so that it will correctly handle iterable inputs as well as no inputs and nil inputs? For example, I'd like to be able to support:
fruits_of_kind(nil) # => nil
fruits_of_kind(:apple, :banana) # => all apples and bananas in basket
fruits_of_kind([:apple, 'banana']) # => likewise
Is this possible to do idiomatically? If so, what's the best way to write methods so that they can accept zero, one, or many inputs?
You need to use the Ruby splat operator, which wraps all remaining arguments into an Array and passes them in:
def foo (a, b, *c)
#do stuff
end
foo(1, 2) # a = 1, b = 2, c = []
foo(1, 2, 3, 4, 5) #a = 1, b = 2, c = [3, 4, 5]
In your case, something like this should work:
def fruits_of_kind(*kinds)
kinds.flatten!
basket.select do |fruit|
kinds.each do |kind|
break true if fruit.fruit_type == kind.to_s
end == true #if no match is found, each returns the whole array, so == true returns false
end
end
EDIT
I changed the code to flatten kinds so that you can send in a list. This code will handle entering no kinds at all, but if you want to expressly input nil, add the line kinds = [] if kinds.nil? at the beginning.
Use the VARARGS feature of Ruby.
# Retrieve all fruits from basket that are of the specified kind.
# notice the * prefix used for method parameter
def fruits_of_kind(*kind)
kind.each do |x|
puts x
end
end
fruits_of_kind(:apple, :orange)
fruits_of_kind()
fruits_of_kind(nil)
-sasuke
def fruits_of_kind(kind)
return nil if kind.nil?
result = []
([] << kind).flatten.each{|k| result << basket.select{|f| f.fruit_type == k.to_s }}
result
end
The 'splat' operator is probably the best way to go, but there are two things to watch out for: passing in nil or lists. To modify Pesto's solution for the input/output you'd like, you should do something like this:
def fruits_of_kind(*kinds)
return nil if kinds.compact.empty?
basket.select do |fruit|
kinds.flatten.each do |kind|
break true if fruit.fruit_type == kind.to_s
end == true #if no match is found, each returns the whole array, so == true returns false
end
end
If you pass in nil, the * converts it to [nil]. If you want to return nil instead of an empty list, you have to compact it (remove nulls) to [], then return nil if it's empty.
If you pass in a list, like [:apple, 'banana'], the * converts it to [[:apple, 'banana']]. It's a subtle difference, but it's a one-element list containing another list, so you need to flatten kinds before doing the "each" loop. Flattening will convert it to [:apple, 'banana'], like you expect, and give you the results you're looking for.
EDIT: Even better, thanks to Greg Campbell:
def fruits_of_kind(basket, kind)
return nil if kind.nil?
kind_list = ([] << kind).flatten.map{|kind| kind.to_s}
basket.select{|fruit| kind_list.include?(fruit) }
end
OR (using splat)
def fruits_of_kind(*kinds)
return nil if kinds.compact.empty?
kind_list = kinds.flatten.map{|kind| kind.to_s}
basket.select{|fruit| kind_list.include?(fruit.fruit_type) }
end
There's a nicely expressive use of splat as an argument to array creation that handles your last example:
def foo(may_or_may_not_be_enumerable_arg)
arrayified = [*may_or_may_not_be_enumerable_arg]
arrayified.each do |item|
puts item
end
end
obj = "one thing"
objs = ["multiple", "things", 1, 2, 3]
foo(obj)
# one thing
# => ["one thing"]
foo(objs)
# multiple
# things
# 1
# 2
# 3
# => ["multiple", "things", 1, 2, 3]