Ruby - mapping an array to hashmap - ruby

I have an array, and a function that returns a value given a value. Ultimately I want to create a hashmap that has the values of the array as key value, and the result of f(key_value) as the value. Is there a clean, simple way, like similar to each/map of Array, of doing this using block?
So something that is equivalent to
hsh = {}
[1,2,3,4].each do |x|
hsh[x] = f(x)
end
but looks more similar to this, in that it's simple and one line?
results = array.map { | x | f(x) }

Note that since Ruby 2.1.0 you can also use Array#to_h, like this:
[1,2,3,4].map{ |x| [x, f(x)] }.to_h

Ruby 2.6.0 enables passing a block to the to_h-method. This enables an even shorter syntax for creating a hash from an array:
[1, 2, 3, 4].to_h { |x| [x, f(x)] }

You could also define the function as the hash's default value:
hash = Hash.new {|hash, key| hash[key] = f(key) }
Then when you lookup a value, the hash will calculate and store it on the fly.
hash[10]
hash.inspect #=> { 10 => whatever_the_result_is }

You need each_with_object.
def f x
x * 2
end
t = [1, 2, 3, 4].each_with_object({}) do |x, memo|
memo[x] = f(x)
end
t # => {1=>2, 2=>4, 3=>6, 4=>8}
Another one:
t2 = [1, 2, 3, 4].map{|x| [x, f(x)]}
Hash[t2] # => {1=>2, 2=>4, 3=>6, 4=>8}

Check out the Hash::[] method.
Hash[ [1,2,3,4].collect { |x| [x, f(x)] } ]

Using Facets' mash (method to convert enumerable to hashes):
[1, 2, 3, 4].mash { |x| [x, f(x)] }
From Ruby 2.1:
[1, 2, 3, 4].map { |x| [x, f(x)] }.to_h

Also, Rails method index_with would be helpful:
a = ['a', 'bsdf', 'wqqwc']
a.index_with(&:size)
=> {"a"=>1, "bsdf"=>4, "wqqwc"=>5}

You're looking for reduce()|inject() method:
elem = [1,2,3,4]
h = elem.reduce({}) do |res, x|
res[x] = x**2
res
end
puts h
The argument passed to reduce({}) is the initial value of an intermediate object that is passed to the block as res variable. In each iteration we're adding new pair key: value to the res Hash and returing the Hash to be used in next iteration.
The method above precomputes a very practical hash of squared values:
{1=>1, 2=>4, 3=>9, 4=>16}

Related

Stable #sort taking a block

We find here an implementation of a stable sort_by in Ruby, which works for the general case (i.e. I can supply my own comparision algorithm), and in this thread user tokland describes a very elegant way to do a stable sort_by:
module Enumerable
def stable_sort_by
sort_by.with_index { |x, idx| [yield(x), idx] }
end
end
The idea of using an Enumerator object together with with_index is surprisingly simple! I would like to find a similar elegant solution to create a stable version of the #sort function where it is given a comparison block. It would be used like this:
sorted_people = people.stable_sort do |person|
person.name
end
Here's a solution (but far from elegant):
module Enumerable
def stable_sort
each_with_index.sort { |(x, i), (y, j)|
r = yield(x, y)
r == 0 ? i <=> j : r
}.map(&:first)
end
end
It generates an array of [element, index] pairs and sorts them by passing each two elements to the given block (just like sort does). If the block returns 0, it compares the indices, otherwise, it returns the block's result. Afterwards, the elements are extracted from the generated array.
Example:
arr = [[2, :baz], [1,:foo], [1, :bar]]
arr.sort { |x, y| x[0] <=> y[0] }
#=> [[1, :bar], [1, :foo], [2, :baz]]
arr.stable_sort { |x, y| x[0] <=> y[0] }
#=> [[1, :foo], [1, :bar], [2, :baz]]

How do I create a hash where the keys are values from an array Ruby

I have an array:
arr = [a, ab, abc]
I want to make a hash, using the values of the array as the keys:
newhash = [a[1], ab[1], abc[1]]
I have tried:
arr.each do |r|
newhash[r] == 1
end
to no avail.
How would I about accomplishing this in ruby?
If you are feeling like a one-liner, this will work as well
h = Hash[arr.collect { |v| [v, 1] } ]
collect is invoked once per element in the array, so it returns an array of 2-element arrays of key-value pairs.
Then this is fed to the hash constructor, which turns the array of pairs into a hash
You could also use the #reduce method from Enumerable (which is included into the Array class).
new_hash = arr.reduce({}) { |hsh, elem| hsh[elem] = 1; hsh }
And your new_hash looks like this in Ruby:
{"a": 1, "ab": 1, "abc": 1}
== is comparison. = is assigning. So just modify == into =. It works.
newhash = {}
arr.each do |r|
newhash[r] = 1
end
(I believe a, ab, abc are strings)
To learn more, this might help you. Array to Hash Ruby
You can do it like this:
ary = [[:foo, 1], [:bar, 2]]
Hash[ary] # => {:foo=>1, :bar=>2}
If you want to do it like you tried earlier, you want to initialize hash correctly:
ary = [:foo, :bar]
hash = {}
ary.each do |key|
hash[key] = 1
end # => {:foo=>1, :bar=>2}

Ruby: Mapping a Hash

In Python, I can create a test hash with list comprehension that I check against a suite of test(s). How can I achieve the same thing in ruby? (I'm running on ruby 1.9.3)
Python:
test = {x: self.investor.annual_return(x) for x in xrange(1, 6)}
Ruby (attempt):
test = Hash[(1..5).map { |x| [x, #investor.annual_return(x)] }]
You want something like:
test = {}
(1..5).map { |i| test[i] = #investor.annual_return(i) }
I think your Ruby code is fine, depending on what version of Ruby you're running.
Starting with:
class Investor
def annual_return(i)
i * i
end
end
investor = Investor.new
In Ruby 1.9+, this will work:
test = Hash[ (1..5).map { |x| [x, investor.annual_return(x)] } ]
test # => {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
However, prior to 1.9, Hash wouldn't convert an array of arrays containing key/value pairs, so we had to get a bit fancier, and flatten the nested elements into a single array, then "explode" those elements for Hash:
test = Hash[ *(1..5).map { |x| [x, investor.annual_return(x)] }.flatten ]
test # => {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
The result is the same, it's just less hassle these days.
And, just to show what Ruby does as we build a hash this way:
(1..5).map { |x| [x, investor.annual_return(x)] }
# => [[1, 1], [2, 4], [3, 9], [4, 16], [5, 25]]
(1..5).map { |x| [x, investor.annual_return(x)] }.flatten
# => [1, 1, 2, 4, 3, 9, 4, 16, 5, 25]
You often see:
test = (1..5).reduce({}) {|h, x| h[x] = #investor.annual_return(x); h}
but (since Ruby 1.9) many prefer Enumerable#each_with_object:
test = (1..5).each_with_object({}) {|x, h| h[x] = #investor.annual_return(x)}
in part because there is no need to return the object h to the iterator, as there is with Enumerable#reduce (aka inject).
If I understand correctly what you're trying to do, you could try this:
{}.tap { |x| (1..5).each do |y| x[y] = #investor.annual_return(i) end }
You can do it easily with:
(1..5).map { |x| [x, #investor.annual_return(x)] }.to_h
(Doc: Array#to_h)
Hash[*array] is used to construct a hash from a flat array ([key1, value1, key2, value2, keyN, valueN]), whereas Array#to_h is used to construct a hash from an array of key-value pairs ([ [key1, value1], [key2, value2], [keyN, valueN] ]).

Turn an array into keys for hash

How do I turn an Array into a Hash with values of 0 without an each loop.
For example, given this array:
[1, 2, 3, 4]
I want to get this hash:
{"1"=>0, "2"=>0, "3"=>0, "4"=>0}
The standard approach is Hash[...]:
Hash[xs.map { |x| [x.to_s, 0] }]
Or Enumerable#mash if you happen to use Facets. I cannot think of something more concise and declarative:
xs.mash { |x| [x.to_s, 0] }
array.inject({}) { | a, e | a[e.to_s] = 0; a }
or in a more clean way (thanks to tokland, see the discussion in the comments)
array.inject({}) { | a, e | a.update(e.to_s => 0) }
I'm a fan of simple, and I can never remember exactly how crazy things #inject or Hash constructor arguments work.
array = [1, 2, 3, 4]
hash = {}
array.each do |obj|
hash[obj.to_s] = 0
end
puts hash.inspect # {"1"=>0, "2"=>0, "3"=>0, "4"=>0}
Okay, in reality, I'd use each_with_object, but posting this since it's more fun.
ary = *1..4
hash = Hash[ary.zip ary.dup.fill 0]
hash # => {1=>0, 2=>0, 3=>0, 4=>0}

Overriding elements of an array in "each" loop

a = [1, 2, 3]
a.each do |x| x+=10 end
After this operation array a is still [1, 2, 3]. How to convert it into [11, 12, 13]?
Use the collect! method:
a = [1, 2, 3]
a.collect!{ |x| x + 10 }
There are two general classes of solutions:
Imperative object-mutating code
a.map! { |x| x + 10 }
An almost functional solution
a = a.map { |x| x + 10 }
Both techniques have their place.
I like the aliased name "map" myself. It has less characters.
The difference with these methods as compared to what you've done is two fold. One is that you have to use a method that modifies the initial array (typically these are the bang methods, or the methods which have a name ending in a ! (map!, collect!, ...) The second thing is that a.each is the method typically used for just going through the array to use the individual elements. Map or Collect methods return an array containing a return from each iteration of the block.
Hence, you could have done the following:
a = [1,2,3]
b = []
a.each do |x|
b << x+10
end
or you could use the map or collect method as demonstrated by dmarko or as here:
a = [1,2,3]
a = a.map {|x| x+10}

Resources