This is my code so far:
class Integer
def atm
money = %w[e500 e200 e100 e50 e20 e10 e5]
return '' if self == 0
money.each do |m|
value = m.split('e').last.to_i
next if self.to_f / value < 1.0
anm = self / value
erg = ("e" + value.to_s)
return money << {erg => anm}, (self-anm*value).atm
end
end
end
puts 140.atm
It gives this output:
e500
e200
e100
e50
e20
e10
e5
{"e100"=>1}
e500
e200
e100
e50
e20
e10
e5
{"e20"=>2}
But I just want this:
[{ "e100" => 1 },{ "e20" => 2 }]
How can I eliminate the rest?
You can get the desired output by removing money << from your return statement, i.e.:
return {erg => anm}, (self-anm*value).atm
Which gives:
puts 140.atm
{"e100"=>1}
{"e20"=>2}
However, puts hides the fact that the result is actually a nested array with an empty string:
p 140.atm
[{"e100"=>1}, [{"e20"=>2}, ""]]
To fix this, you can return nil if self is 0:
return nil if self == 0
# or simply
return if self == 0
And use the splat operator * on the second element for return to "flatten" the return value:
return {erg => anm}, *(self-anm*value).atm
# ^
Which gives:
p 140.atm
[{"e100"=>1}, {"e20"=>2}]
Another option is to avoid the recursion and to collect the result within the loop, e.g.:
def atm
money = %w[e500 e200 e100 e50 e20 e10 e5]
i = self
money.each_with_object([]) do |m, result|
value = m.split('e').last.to_i
anm, i = i.divmod(value)
result << { m => anm } if anm > 0
end
end
I'm using divmod here to calculate the quotient and remainder (actually the modulus) at once. It works like this:
140.divmod(500) #=> [0, 140]
140.divmod(200) #=> [0, 140]
140.divmod(100) #=> [1, 40]
40.divmod( 50) #=> [0, 40]
40.divmod( 20) #=> [2, 0]
The result's first element determines who many times the arguments "fits" into the receiver. Its second element is the remainder which also becomes the new left-hand value.
For example:
140.divmod(100) #=> [1, 40]
# means: 140 = 100 * 1 + 40
Object to be returned
You may find it more convenient to return a hash rather than an array of hashes. For example,
645.atm
#=> { 500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>1 }
0.atm
#=> { 500=>0, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0 }
unless there is no way to convert the given amount to different amounts of the given denominations, in which case nil is returned:
646.atm
#=> nil
I assume that, before Integer#atm is called, it has been confirmed that the amount to be changed (self) is a non-negative integer.
Integer#atm method
We may write:
class Integer
def atm
denominations = [500, 200, 100, 50, 20, 10, 5]
change = denominations.product([0]).to_h
amount_to_change = self
denominations.each do |d|
change[d], amount_to_change = amount_to_change.divmod(d)
return change if amount_to_change.zero?
end
nil
end
end
See Array#product, Array#to_h and Integer#divmod, the latter being a very useful, yet underutilized, method.
Try it
Let's try it, but first I will add some puts statements to illustrate the intermediate calculations.
class Integer
def atm
denominations = [500, 200, 100, 50, 20, 10, 5]
change = denominations.product([0]).to_h
puts "change=#{change}"
amount_to_change = self
puts "Initial amount_to_change=#{amount_to_change}"
denominations.each do |d|
change[d], amount_to_change = amount_to_change.divmod(d)
puts "d=#{d}, change=#{change}, amount_to_change=#{amount_to_change}"
return change if amount_to_change.zero?
end
nil
end
end
645.atm
change={500=>0, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}
Initial amount_to_change=645
d=500, change={500=>1, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}, amount_to_change=145
d=200, change={500=>1, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}, amount_to_change=145
d=100, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>0, 10=>0, 5=>0}, amount_to_change=45
d=50, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>0, 10=>0, 5=>0}, amount_to_change=45
d=20, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>0}, amount_to_change=5
d=10, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>0}, amount_to_change=5
d=5, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>1}, amount_to_change=0
#=> {500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>1}
0.atm
change={500=>0, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}
Initial amount_to_change=0
#=> {500=>0, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}
646.atm
change={500=>0, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}
Initial amount_to_change=646
d=500, change={500=>1, 200=>0, 100=>0, 50=>0, 20=>0, 10=>0, 5=>0}, amount_to_change=146
...
d=5, change={500=>1, 200=>0, 100=>1, 50=>0, 20=>2, 10=>0, 5=>1}, amount_to_change=1
#=> nil
Make code more Ruby-like by using Enumerable#each_with_object
You will find that the method Enumerable#each_with_object is widely used in Ruby code. Here it would be used as follows.
class Integer
def atm
denominations = [500, 200, 100, 50, 20, 10, 5]
amount_to_change = self
denominations.each_with_object(denominations.product([0]).to_h) do |d,change|
change[d], amount_to_change = amount_to_change.divmod(d)
return change if amount_to_change.zero?
end
nil
end
end
Use a default value for hash keys
Another change you might consider is use Hash::new to define the hash to have a default value of zero:
change = Hash.new(0)
#=> {}
This merely causes change[k] to return 0 when the hash has no key k. The hash is not altered:
change[500]
#=> 0
change
#=> {}
change[500] = 1
change
#=> {500=>1}
We may now change the method atm as follows.
class Integer
def atm
denominations = [500, 200, 100, 50, 20, 10, 5]
amount_to_change = self
puts "Initial amount to change=#{amount_to_change}"
denominations.each_with_object(Hash.new(0)) do |d,change|
chg, amount_to_change = amount_to_change.divmod(d)
change[d] = chg if chg > 0
puts "d=#{d}, change=#{change}, amount_to_change=#{amount_to_change}"
return change if amount_to_change.zero?
end
nil
end
end
645.atm
Initial amount to change=645
d=500, change={500=>1}, amount_to_change=145
d=200, change={500=>1}, amount_to_change=145
d=100, change={500=>1, 100=>1}, amount_to_change=45
d=50, change={500=>1, 100=>1}, amount_to_change=45
d=20, change={500=>1, 100=>1, 20=>2}, amount_to_change=5
d=10, change={500=>1, 100=>1, 20=>2}, amount_to_change=5
d=5, change={500=>1, 100=>1, 20=>2, 5=>1}, amount_to_change=0
#=> {500=>1, 100=>1, 20=>2, 5=>1}
If we compute change = 645.atm, and need the number of units of a given denomination we simply write, for example,
change[500]
#=> 1
change[50]
#=> 0 (default value)
We must be mindful, however, that
change["cat"]
#=> 0
Avoid polluting the core class Integer
Monkey patching--adding or changing core methods--is generally frowned upon. Better to write, for example,
def atm(amount_to_change)
denominations = [500, 200, 100, 50, 20, 10, 5]
denominations.each_with_object(Hash.new(0)) do |d,change|
chg, amount_to_change = amount_to_change.divmod(d)
change[d] = chg if chg > 0
return change if amount_to_change.zero?
end
nil
end
Another option is to use Refinements.
Related
Is there an "easy" way, short of hand-writing the kind of nested Hash/Array traversal performed by Hash#dig, that I can determine if a key is present in a deeply nested Hash? Another way to ask this is to say "determine if any value is assigned".
There is a difference between a Hash having nothing assigned, or it having an explicit nil assigned - especially if the Hash were constructed with a different missing key default value than nil!
h = { :one => { :two => nil }}
h.dig(:one, :two).nil? # => true; but :two *is* present; it is assigned "nil".
h[:one].key?(:two) # => true, because the key exists
h = { :one => {}}
h.dig(:one, :two).nil? # => true; :two *is not* present; no value is assigned.
h[:one].key?(:two) # => FALSE, because the key does not exist
If you are purely checking the existence of a key, you can combine dig and key?. Use key? on the final or last key in your series of keys.
input_hash = {
hello: {
world: {
existing: nil,
}
}
}
# Used !! to make the result boolean
!!input_hash.dig(:hello, :world)&.key?(:existing) # => true
!!input_hash.dig(:hello, :world)&.key?(:not_existing) # => false
!!input_hash.dig(:hello, :universe)&.has_key?(:not_existing) # => false
Inspired by your core extension suggestion I updated the implementation a bit to better mimic that of #dig
requires 1+ arguments
raises TypeError if the dig does not return nil, the resulting object does not respond to dig? and there are additional arguments to be "dug"
module Diggable
def dig?(arg,*args)
return self.member?(arg) if args.empty?
if val = self[arg] and val.respond_to?(:dig?)
val.dig?(*args)
else
val.nil? ? false : raise(TypeError, "#{val.class} does not have a #dig? method")
end
end
end
[Hash,Struct,Array].each { |klass| klass.send(:include,Diggable) }
class Array
def dig?(arg,*args)
return arg.abs < self.size if args.empty?
super
end
end
if defined?(OpenStruct)
class OpenStruct
def dig?(arg,*args)
self.to_h.dig?(arg,*args)
end
end
end
Usage
Foo = Struct.new(:a)
hash = {:one=>1, :two=>[1, 2, 3], :three=>[{:one=>1, :two=>2}, "hello", Foo.new([1,2,3]), {:one=>{:two=>{:three=>3}}}]}
hash.dig? #=> ArgumentError
hash.dig?(:one) #=> true
hash.dig?(:two, 0) #=> true
hash.dig?(:none) #=> false
hash.dig?(:none, 0) #=> false
hash.dig?(:two, -1) #=> true
hash.dig?(:two, 10) #=> false
hash.dig?(:three, 0, :two) #=> true
hash.dig?(:three, 0, :none) #=> false
hash.dig?(:three, 2, :a) #=> true
hash.dig?(:three, 3, :one, :two, :three, :f) #=> TypeError
Example
For reference - taking the unusual step of answering my own question ;-) - here's one of several ways I could solve this if I just wanted to write lots of Ruby.
def dig?(obj, *args)
arg = args.shift()
return case obj
when Array
if args.empty?
arg >= 0 && arg <= obj.size
else
dig?(obj[arg], *args)
end
when Hash
if args.empty?
obj.key?(arg)
else
dig?(obj[arg], *args)
end
when nil
false
else
raise ArgumentError
end
end
Of course, one could also have opened up classes like Array and Hash and added #dig? to those, if you prefer core extensions over explicit methods:
class Hash
def dig?(*args)
arg = args.shift()
if args.empty?
self.key?(arg)
else
self[arg]&.dig?(*args) || false
end
end
end
class Array
def dig?(*args)
arg = args.shift()
if args.empty?
arg >= 0 && arg <= self.size
else
self[arg]&.dig?(*args) || false
end
end
end
...which would raise NoMethodError rather than ArgumentError if the #dig? arguments led to a non-Hash/Array node.
Obviously it would be possible to compress those down into more cunning / elegant solutions that use fewer lines, but the above has the benefit of IMHO being pretty easy to read.
In the scope of the original question, though, the hope was to lean more on anything Ruby has out-of-the-box. We've collectively acknowledged early-on that there is no single-method solution, but the answer from #AmazingRein gets close by reusing #dig to avoid recursion. We might adapt that as follows:
def dig?(obj, *args)
last_arg = args.pop()
obj = obj.dig(*args) unless args.empty?
return case obj
when Array
last_arg >= 0 && last_arg <= obj.size
when Hash
obj.key?(last_arg)
when nil
false
else
raise ArgumentError
end
end
...which isn't too bad, all things considered.
# Example test...
hash = {:one=>1, :two=>[1, 2, 3], :three=>[{:one=>1, :two=>2}, "hello", {:one=>{:two=>{:three=>3}}}]}
puts dig?(hash, :one)
puts dig?(hash, :two, 0)
puts dig?(hash, :none)
puts dig?(hash, :none, 0)
puts dig?(hash, :two, -1)
puts dig?(hash, :two, 10)
puts dig?(hash, :three, 0, :two)
puts dig?(hash, :three, 0, :none)
puts dig?(hash, :three, 2, :one, :two, :three)
puts dig?(hash, :three, 2, :one, :two, :none)
Here is a concise way of doing it which works with nested Array and Hash (and any other object that responds to fetch).
def deep_fetch? obj, *argv
argv.each do |arg|
return false unless obj.respond_to? :fetch
obj = obj.fetch(arg) { return false }
end
true
end
obj = { hello: [ nil, { world: nil } ] }
deep_fetch? obj, :hell # => false
deep_fetch? obj, :hello, 0 # => true
deep_fetch? obj, :hello, 2 # => false
deep_fetch? obj, :hello, 0, :world # => false
deep_fetch? obj, :hello, 1, :world # => true
deep_fetch? obj, :hello, :world
TypeError (no implicit conversion of Symbol into Integer)
The previous code raises an error when accessing an Array element with a non-Integer index (just like Array#dig), which sometimes is not the behavior one is looking for. The following code works well in all cases, but the rescue is not a good practice:
def deep_fetch? obj, *argv
argv.each { |arg| obj = obj.fetch(arg) } and true rescue false
end
obj = { hello: [ nil, { world: nil } ] }
deep_fetch? obj, :hell # => false
deep_fetch? obj, :hello, 0 # => true
deep_fetch? obj, :hello, 2 # => false
deep_fetch? obj, :hello, 0, :world # => false
deep_fetch? obj, :hello, 1, :world # => true
deep_fetch? obj, :hello, :world # => false
I was creating a ruby programme that will calculate the frequency of each letter appearing in my text and will be return it as a Hash.
Below is my code:
class LetterHistogram
attr_reader :letters
attr_accessor :text
def initialize(t = "Hello World!")
#text = t
end
def display
calculateFrequencies
("A".."Z").each {|x| puts "#{x}: " + "*" * letters[x]}
end
private
attr_writer :letters
def calculateFrequencies
calcuFreq = String.new(text)
calcuFreq.upcase!.gsub!(/\W+/, '')
letters.clear
letters.default = 0
calcuFreq.each_char {|char| letters[char] += 1}
end
end
But I getting this error when I run the display method
enter image description here
What is the error means and how to solve it?
The main problem is that in calculateFrequencies you are using a not assigned variable: letters. So, when you call calculateFrequencies in display, letters = nil and calling .clear on nil returns the error.
This is a modified version of the code, using snake_case (which is the Ruby writing standard).
class LetterHistogram
attr_accessor :text
def initialize(t = "Hello World!")
#text = t
end
def display
calculate_frequencies.each { |letter, freq| puts "#{letter}: #{freq}"}
end
private
def calculate_frequencies
freq = #text.upcase.gsub!(/\W+/, '').each_char.with_object(Hash.new(0)) { |letter, freq| freq[letter] += 1 }
freq.sort_by{ |letter, freq| freq }.reverse # just to sort
end
end
Instantiating an object and calling .display on it:
senctence = LetterHistogram.new
senctence.display
#=> L: 3
#=> O: 2
#=> D: 1
#=> R: 1
#=> W: 1
#=> E: 1
#=> H: 1
How it works
For calculating the frequency I used a Hash populated by: https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-each_with_object
Printing out freq from calculate_frequencies you can see it:
#=> {"H"=>1, "E"=>1, "L"=>3, "O"=>2, "W"=>1, "R"=>1, "D"=>1}
Alternatively, if you want also not used letters, you can initialise the freq hash with all values to 0, then update the hash, something like this:
freq = ("A".."Z").each_with_object({}) { |letter, freq| freq[letter] = 0 }
"Hello World!".upcase.gsub!(/\W+/, '').each_char { |letter| freq[letter] += 1 }
#=> {"A"=>0, "B"=>0, "C"=>0, "D"=>1, "E"=>1, "F"=>0, "G"=>0, "H"=>1, "I"=>0, "J"=>0, "K"=>0, "L"=>3, "M"=>0, "N"=>0, "O"=>2, "P"=>0, "Q"=>0, "R"=>1, "S"=>0, "T"=>0, "U"=>0, "V"=>0, "W"=>1, "X"=>0, "Y"=>0, "Z"=>0}
Finally, to print out the istogram:
freq.each { |letter, freq| puts "#{letter}: " + "◼︎" * freq if freq > 0 }
#=> D: ◼︎
#=> E: ◼︎
#=> H: ◼︎
#=> L: ◼︎◼︎◼︎
#=> O: ◼︎◼︎
#=> R: ◼︎
#=> W: ◼︎
I have a function that generates a random email address:
def emails
names = ["alfred", "daniel", "elisa", "ana", "ramzes"]
surnames = ["oak", "leaf", "grass", "fruit"]
providers = ["gmail", "yahoo", "outlook", "icloud"]
address = "#{names.sample}.#{surnames.sample}#{rand(100..5300)}##{providers.sample}.com"
end
Given a list of randomly generated email address:
email_list = 100.times.map { emails }
that looks like this:
daniel.oak3985#icloud.com
ramzes.grass1166#icloud.com
daniel.fruit992#yahoo.com
...
how can I select the most common provider ("gmail", "yahoo", etc.)?
Your question is similar to this one. There's a twist though : you don't want to analyze the frequency of email addresses, but their providers.
def random_email
names = ["alfred", "daniel", "elisa", "ana", "ramzes"]
surnames = ["oak", "leaf", "grass", "fruit"]
providers = ["gmail", "yahoo", "outlook", "icloud"]
address = "#{names.sample}.#{surnames.sample}#{rand(100..5300)}##{providers.sample}.com"
end
emails = Array.new(100){ random_email }
freq = emails.each_with_object(Hash.new(0)) do |email,freq|
provider = email.split('#').last
freq[provider] += 1
end
p freq
#=> {"outlook.com"=>24, "yahoo.com"=>28, "gmail.com"=>32, "icloud.com"=>16}
p freq.max_by{|provider, count| count}.first
#=> "gmail.com"
email_list = 10.times.map { emails }
#=> ["alfred.grass426#gmail.com", "elisa.oak239#icloud.com",
# "daniel.fruit1600#outlook.com", "ana.fruit3761#icloud.com",
# "daniel.grass742#yahoo.com", "elisa.oak3891#outlook.com",
# "alfred.leaf1321#gmail.com", "alfred.grass5295#outlook.com",
# "ramzes.fruit435#gmail.com", "ana.fruit4233#yahoo.com"]
email_list.group_by { |s| s[/#\K.+/] }.max_by { |_,v| v.size }.first
#=> "gmail.com"
\K in the regex means disregard everything matched so far. Alternatively, #\K could be replaced by the positive lookbehind (?<=#).
The steps are as follows.
h = email_list.group_by { |s| s[/#\K.+/] }
#=> {"gmail.com" =>["alfred.grass426#gmail.com", "alfred.leaf1321#gmail.com",
# "ramzes.fruit435#gmail.com"],
# "icloud.com" =>["elisa.oak239#icloud.com", "ana.fruit3761#icloud.com"],
# "outlook.com"=>["daniel.fruit1600#outlook.com", "elisa.oak3891#outlook.com",
# "alfred.grass5295#outlook.com"],
# "yahoo.com" =>["daniel.grass742#yahoo.com", "ana.fruit4233#yahoo.com"]}
a = h.max_by { |_,v| v.size }
#=> ["gmail.com", ["alfred.grass426#gmail.com", "alfred.leaf1321#gmail.com",
# "ramzes.fruit435#gmail.com"]]
a.first
#=> "gmail.com"
If, as here, there is a tie for most frequent, modify the code as follows to get all winners.
h = email_list.group_by { |s| s[/#\K.+/] }
# (same as above)
mx_size = h.map { |_,v| v.size }.max
#=> 3
h.select { |_,v| v.size == mx_size }.keys
#=> ["gmail.com", "outlook.com"]
Please help to explain what is needed in my code to decipher if the array contains an integer, if it does I need to add 1 to it and if doesn't if will just display the string or symbol.
I have left #notes where my brain stopped working
# possible arrays
# array = [1, "two", :three]
# array = [1, 2, 3]
class Array
def new_map
result = []
self.each do |item|
yield(item)
if #check to see if item is an integer
result << item + 1
else
# add string to result array
end
end
result
end
end
Here is the Rspec test:
describe "Array" do
describe "new_map" do
it "should not call map" do
a = [1, 2, 3]
a.stub(:map) { '' }
a.new_map { |i| i + 1 }.should eq([2, 3, 4])
end
it "should map any object" do
a = [1, "two", :three]
a.new_map { |i| i.class }.should eq([Fixnum, String, Symbol])
end
end
end
if item.is_a? Integer
result << item + 1
class Array
def new_map
result = []
self.each do |item|
yield(item)
if item.class == Integer # or if item.is_a? Integer
result << item + 1
else
# add string to result array
end
end
result
end
end
example:
=> 1.is_a? Integer
=> true
=> "1".is_a? Integer
=> false
=> 1_000_000.is_a? Integer
=> true
=> 1_000_000.class
=> Fixnum
=> 1_000_000.is_a? Integer
=> true
Try this:
class Array
def new_map
map do |item|
yield(item)
end
end
end
Actually you first spec does not make sense. You yield i + 1 to the block. This must fail if ì is not a Fixnum. You must not check if something is an Integer in your method, but in the block. This should work:
describe "Array" do
describe "new_map" do
it "should not call map" do
a = [1, 2, 3]
a.new_map { |i| (i.is_a?(Integer) ? i + 1 : i) }.should eq([2, 3, 4])
end
it "should map any object" do
a = [1, "two", :three]
a.new_map { |i| i.class }.should eq([Fixnum, String, Symbol])
end
end
end
class Array
def new_map
result = []
self.each do |item|
result << yield(item)
end
result
end
end
And both your tests pass, don't check object's class unnecessarily, trust duck typing.
We create a method with splatted arguments and call Method#parameters on it:
def splatter(x, *y, z); end
params = method(:splatter).parameters
# => [[:req, :x], [:rest, :y], [:req, :z]]
I'm looking for a function f that will map a list of arguments onto their corresponding variable names. The function should be flexible enough to work on any other method with arbitrarily placed splat arguments. For example:
args = [:one, :two, :three, :four]
f(params, args)
# => [[:x, :one], [:y, :two], [:y, :three], [:z, :four]]
or something along those lines (flipped elements would be fine also). I feel there must be a flexible, elegant solution using inject or something, but I can't seem to come up with it.
def f(params,*args)
# elements to be assigned to splat parameter
splat = args.count - params.count + 1
# will throw an error if splat < 0 as that means not enough inputs given
params.map{ |p|
[ p[1] , ( p.first == :rest ? args.shift(splat) : args.shift ) ]
}
end
Examples
def splatter(x,*y,z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
#=>[[:x, 1], [:y, [2, 3]], [:z, 4]]
def splatter(x,y,*z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
# => [[:x, 1], [:y, 2], [:z, [3, 4]]]
def splatter(x,*z)
# some code
end
f(method(:splatter).parameters, 1)
# => [[:x, 1], [:z, []]]
I think this is a good example where eval can be useful. The code below generates a lambda which takes the same arguments as specified and spits out the resolved list of arguments. The advantage of this approach is that Ruby's own algorithm for resolving splats is used.
def resolve(parameters,args)
param_list = parameters.map do |type,name|
splat = '*' if type == :rest
"#{splat}#{name}"
end.join(',')
source = ""
source << "->(#{param_list}) do\n"
source << " res = []\n"
parameters.each do |type,name|
if type == :rest
source << " res += #{name}.map {|v| [:'#{name}',v] }\n"
else
source << " res << [:'#{name}',#{name}]\n"
end
end
source << "end"
eval(source).call(*args)
end
Example:
params = ->(x,*y,z){}.parameters
resolve(params,[:one, :two, :three, :four])
#=> [[:x, :one], [:y, :two], [:y, :three], [:z, :four]]
Behind the scenes, the following code was generated:
->(x,*y,z) do
res = []
res << [:'x',x]
res += y.map {|v| [:'y',v] }
res << [:'z',z]
end
Another example with two arguments, splat first:
params = ->(*x,y){}.parameters
resolve(params,[:one, :two, :three, :four])
#=> [[:x, :one], [:x, :two], [:x, :three], [:y, :four]]
With the generated code being
->(*x,y) do
res = []
res += x.map {|v| [:'x',v] }
res << [:'y',y]
end
Edit: After my initial confusion:
def doit(params, args)
rest_ndx = params.map(&:first).index(:rest)
to_insert = [params[rest_ndx].last]*(args.size-params.size) if rest_ndx
params = params.map(&:last)
params.insert(rest_ndx,*to_insert) if rest_ndx
params.zip(args)
end