Ruby's Unary * Operator [duplicate] - ruby

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is the * operator doing to this string in Ruby
I ran across the following code when looking for an easy way to convert an array to a hash (similar to .Net's ToDictionary method on IEnumerable... I wanted to be able to arbitrarily set the key and the value).
a = [ 1, 2, 3, 4, 5, 6 ]
h = Hash[ *a.collect { |v| [ v, v ] }.flatten ]
My question is, what does the asterisk before a.collect do?
By the way, the code comes from http://justatheory.com/computers/programming/ruby/array_to_hash_one_liner.html

It's the splat-operator if you want to google it. It does transform an array into a list (so you can use an array as arguments to a method). It also does the opposite: it can 'slurp' a list into an array.
require 'date'
*date_stuff = 2012,2,29 # slurp
p date_stuff #=> [2012, 2, 29]
Date.new(*date_stuff) # regurgitate

Related

Symbol#to_proc with parameters [duplicate]

This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
Given the following array a:
a = [1, 2, 3, 4, 5]
How do I do:
a.map { |num| num + 1 }
using the short notation:
a.map(&:+ 1)
or:
a.map(&:+ 2)
where 1 and 2 are the arguments?
In this case you can do
a.map(&1.method(:+))
But only because 1 + x is usually the same as x + 1.
Here is a discussion of this practice in a performance context.
You can't do it like this. The & operator is for turning symbols into procs.
a = [1, 2, 3, 4, 5]
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.
& is a shorthand for to_proc:
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.
You cannot do it with map. But look at Facets' Enumerable#map_send:
require 'facets'
[1, 2, 3].map_send(:+, 1)
#=> [2, 3, 4]
Writing your own implementation is pretty straightforward:
module Enumerable
def map_send(*args)
map { |obj| obj.send(*args) }
end
end
If you really need that you can use Ampex library, but I don't know if it is still maintained.

Meaning of * in argument position

Assume arr is an array [[1,3],[2,5],[3,8]]. I was wondering what * means. What does it mean?
hash = Hash[*arr.flatten] # => {1=>3, 2=>5, 3=>8}
I tried the following
arr.flatten # => [1, 3, 2, 5, 3, 8]
Hash[arr.flatten] # => {}
Hash[*a.flatten] # => {1=>3, 2=>5, 3=>8}
Here is the explanation
When you do array.flatten it will give you one flatten array where all inner array slatted. Now You are putting this flatten array inside the Hash::[] method. But Hash::[] supports the below constructs :
Hash[ key, value, ... ] → new_hash
# or
Hash[ [ [key, value], ... ] ] → new_hash
Now array.flatten gives you [1, 3, 2, 5, 3, 8]. Now you are putting this array inside Hash[] like Hash[[1, 3, 2, 5, 3, 8]]. Now compare with the above 2 structures. Does either of them match ? NO. So you need to splat the inner array again, thus it need (splatted operator)* to splat the inner array.
Now you did Hash[*[1, 3, 2, 5, 3, 8]], which gives Hash[1, 3, 2, 5, 3, 8]. Now again check from the above 2 constructs. Does it match with either of the 2 ? This time YES, first one. So you got the desired hash {1=>3, 2=>5, 3=>8}.
BTW, you don't need to splat, as the second construt exactly matched when you put array inside Hash::[] directly.
array = [[1,3],[2,5],[3,8]]
Hash[array] # => {1=>3, 2=>5, 3=>8}
The above worked, because Hash[array] means Hash[[[1,3],[2,5],[3,8]]], which exactly the second structure as documentation suggested.
Read some examples to see how splatting work in Ruby.
There is another construct :-
Hash[ object ] → new_hash
This I think is also important to tell you why you got {}. Hash[[1, 3, 2, 5, 3, 8]] same as the last type of construct as per the doc. The doc is saying -
The second and third form take a single argument which is either an array of key-value pairs or an object convertible to a hash.
So. [1,3,2,5,3,8] it is an Array object not convertible to Hash. Currently it is giving you an empty hash, if an object as per the third construct. But it will throw error in future version of release. See Below warnings.
[1] pry(main)> Hash[[1,2]]
(pry):1: warning: wrong element type Fixnum at 0 (expected array)
(pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
(pry):1: warning: this causes ArgumentError in the next release
(pry):1: warning: wrong element type Fixnum at 1 (expected array)
(pry):1: warning: ignoring wrong elements is deprecated, remove them explicitly
(pry):1: warning: this causes ArgumentError in the next release
=> {}
My Ruby version is :
ruby 2.0.0p451 (2014-02-24 revision 45167) [i686-linux]
When you pass an argument to the Hash class you can put it in parentheses...
Hash(arr.flatten)
or without...
Hash arr.flatten
In either case, Hash takes the argument and if it's an array with an even number of elements, it will create a hash where the odd elements are the keys and the even elements are the values.
Hash can also take square brackets to preform a similar operation
Hash[1, 2]
=> {1=>2}
BUT, when you do this...
Hash[arr.flatten]
You are passing the array WITHIN the array so you're getting unexpected results.
When you do this...
Hash[*arr.flatten]
You are saying take the array arr.flatten and pass it, not as an array, but as individual separate arguments to the method.
So where Hash[arr.flatten] is actually Hash[[1, 3, 2, 5, 3, 8]] (an array containing one element which is an array) instead Hash[*arr.flatten] is actually Hash[1, 3, 2, 5, 3, 8](an array containing six elements)
Interestingly enough, the [] method can take an array containing arrays of key, value pairs so..
Hash[arr]
Works fine! You don't have to flatten or splat anything.

What are the differences between "each", "foreach", "collect" and "map"? [duplicate]

This question already has answers here:
what's different between each and collect method in Ruby [duplicate]
(7 answers)
Closed 8 years ago.
It seems like there are a lot of ways to loop over an Enumerable in Ruby. Are there any differences between each, foreach, or in collect, map or other similar methods?
Or is there a reason I shouldn't use certain methods in certain situations?
collect/map are equivalent. They differ from each in that each only executes the block for each element, whereas collect/map return an array with the results of calling the block for each element. Another way to put it might be, each is expected to do something with each element, whereas map is expected to transform each element (map it onto something else).
You could use collect or map anywhere each is used, and your code will still work. But it will probably be slightly less efficient because it collects the results in an array, unless your Ruby implementation realizes it doesn't have to bother creating an array because it's never used.
Another reason to use each instead of map or collect is to help out anyone reading your code. If I see each then I can be like okay, we're about to use each element of the data to do something. If I see map then I'm expecting to see new data being created, based on a remapping of the old data.
With regards to map vs. collect I would say it's a matter of preference, but you should pick one and stick with it for consistency.
Using pry and Rubinus (it's easier to read ruby code) take a look for yourself
$ pry
[1] pry(main)> cd Enumerable
[2] pry(Enumerable):1> show-method collect
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[3] pry(Enumerable):1> show-method map
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[4] pry(Enumerable):1>
So nope not for these two.
map and collect iterate over a collection. For each object it executes your block then collects the result. The each methods do not collect the results
Array#collect (and Array#map) return a new array based on the code passed in the block. Array#each performs an operation (defined by the block) on each element of the array.
I would use collect like this:
array = [1, 2, 3]
array2 = array.collect {|val| val + 1}
array.inspect # => "[1, 2, 3]"
array2.inspect # => "[2, 3, 4]"
And each like this:
array = [1, 2, 3]
array.each {|val| puts val + 1 }
# >> 2
# >> 3
# >> 4
array.inspect # => "[1, 2, 3]"

How to iterate over part of a hash in Ruby?

h = Hash.new
(1..100).each { |v| h.store(v * 2, v*v) }
What is the best way to iterate over a given part of the hash without using the keys? For example, from element 10 to element 20? Using Ruby 1.9.3.
EDIT - In response to Dave's comment:
Originally I wanted to access the data through keys (hence the hash). But I also want to iterate by element number. BTW, each element is a hash.
So, what is the best way to design a hash of hashes or array of hashes that can be iterated by element number or accessed by key? The data looks like the following. There are missing dates.
6/23/2011 -> 5, 6, 8, 3, 6
6/26/2011 -> 6, 8, 4, 8, 5
6/27/2011 -> 8, 4, 3, 2, 7
If I understand what you're asking for, you can iterate over a portion of your hash as follows. This gives you the 1001st through 2000th values:
h.keys[1000..1999].each do |key|
# Do something with h[key]
end
I think you better use Array for that (Hash in Ruby 1.9.3 are ordered but the access method is the keys). So:
a = h.values
# or
a = h.to_a
Convert it to an array, then slice it:
h.to_a[10..20].each { |k, v| do_stuff }
Note that before Ruby 1.9, the order of elements in a hash are not guaranteed, so this will not necessarily work as you expect.
Alternatively, you could use each_with_index and skip over the unwanted elements:
h.each_with_index do |(k, v), i|
next unless (10..20).include?(i)
# do stuff
end
h = Hash.new
(1..100).each { |v| h.store(v * 2, v*v) }
#for an array of arrays
h.drop(9).take(10) #plus an optional block
#if the slice must be a hash:
slice = Hash[h.drop(9).take(10)]
But if this is an often repeating operation you might be better off using a database.

How do I pass an argument to array.map short cut? [duplicate]

This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
Given the following array a:
a = [1, 2, 3, 4, 5]
How do I do:
a.map { |num| num + 1 }
using the short notation:
a.map(&:+ 1)
or:
a.map(&:+ 2)
where 1 and 2 are the arguments?
In this case you can do
a.map(&1.method(:+))
But only because 1 + x is usually the same as x + 1.
Here is a discussion of this practice in a performance context.
You can't do it like this. The & operator is for turning symbols into procs.
a = [1, 2, 3, 4, 5]
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.
& is a shorthand for to_proc:
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.
You cannot do it with map. But look at Facets' Enumerable#map_send:
require 'facets'
[1, 2, 3].map_send(:+, 1)
#=> [2, 3, 4]
Writing your own implementation is pretty straightforward:
module Enumerable
def map_send(*args)
map { |obj| obj.send(*args) }
end
end
If you really need that you can use Ampex library, but I don't know if it is still maintained.

Resources