Ruby: Mapping a Hash - ruby

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] ]).

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]]

Grouping an array on the basis of its first element, without duplication in Ruby

I'm executing an active record command Product.pluck(:category_id, :price), which returns an array of 2 element arrays:
[
[1, 500],
[1, 100],
[2, 300]
]
I want to group on the basis of the first element, creating a hash that looks like:
{1 => [500, 100], 2 => [300]}
group_by seems logical, but replicates the entire array. I.e. a.group_by(&:first) produces:
{1=>[[1, 500], [1, 100]], 2=>[[2, 300]]}
You can do a secondary transform to it:
Hash[
array.group_by(&:first).collect do |key, values|
[ key, values.collect { |v| v[1] } ]
end
]
Alternatively just map out the logic directly:
array.each_with_object({ }) do |item, result|
(result[item[0]] ||= [ ]) << item[1]
end
This one-liner seemed to work for me.
array.group_by(&:first).map { |k, v| [k, v.each(&:shift)] }.to_h
Since you're grouping by the first element, just remove it with shift and turn the result into a hash:
array.group_by(&:first).map do |key, value|
value = value.flat_map { |x| x.shift; x }
[key, value]
end #=> {1=>[500, 100], 2=>[300]}
I do not like the destructive operation.
array.group_by(&:first).map { |id, a| [id, a.map(&:last)] }.to_h
Used this functionality several times in my app, added extension to an array:
# config/initializers/array_ext.rb
class Array
# given an array of two-element arrays groups second element by first element, eg:
# [[1, 2], [1, 3], [2, 4]].group_second_by_first #=> {1 => [2, 3], 2 => [4]}
def group_second_by_first
each_with_object({}) { |(first, second), h| (h[first] ||= []) << second }
end
end

Remove element from array by type

I have the following array:
["--",1,2,3,4]
How can I remove elements from the array by element type, ie. remove all non-integer values from the array?
I'd do :-
ary = ["--",1,2,3,4]
ary = ary.grep(Integer)
ary # => [1, 2, 3, 4]
Note :- If you don't want to mutate the original array use new_ary instead of ary. Like
new_ary = ary.grep(Integer)
You can use delete_if to remove items from the list, however this modifies the list.
a = ["--", 1, 2, 3, 4]
a.delete_if { |n| !n.kind_of?(Fixnum) }
p a
You can select items out of the list maintaining the original list by using select
a = ["--", 1, 2, 3, 4]
b = a.select { |n| n.kind_of?(Fixnum) }
p b
p a
This solution addresses the title, rather than the example, and permits the selection of elements by class, as well as the rejection of elements by class.
Code
good_classes and bad_classes are arrays of classes.
def filter_select(arr, *good_classes)
arr.select { |e| good_classes.include? e.class }
end
def filter_reject(arr, *bad_classes)
arr.reject { |e| bad_classes.include? e.class }
end
Examples
arr = [1, :a, {b: 3}, "cat", [4,5], true, 3..4, false]
filter_select(arr, Fixnum, Hash, TrueClass, Range)
#=> [1, {:b=>3}, true, 3..4]
filter_reject(arr, Fixnum, Hash, String, Array)
#=> [:a, true, 3..4, false]
I'd do
new_array = ary.reject {|x| x.is_a?(String)}

Ruby - mapping an array to hashmap

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}

Create two-dimensional arrays and access sub-arrays in Ruby

I wonder if there's a possibility to create a two dimensional array and to quickly access any horizontal or vertical sub array in it?
I believe we can access a horizontal sub array in the following case:
x = Array.new(10) { Array.new(20) }
x[6][3..8] = 'something'
But as far as I understand, we cannot access it like this:
x[3..8][6]
How can I avoid or hack this limit?
There are some problems with 2 dimensional Arrays the way you implement them.
a= [[1,2],[3,4]]
a[0][2]= 5 # works
a[2][0]= 6 # error
Hash as Array
I prefer to use Hashes for multi dimensional Arrays
a= Hash.new
a[[1,2]]= 23
a[[5,6]]= 42
This has the advantage, that you don't have to manually create columns or rows. Inserting into hashes is almost O(1), so there is no drawback here, as long as your Hash does not become too big.
You can even set a default value for all not specified elements
a= Hash.new(0)
So now about how to get subarrays
(3..5).to_a.product([2]).collect { |index| a[index] }
[2].product((3..5).to_a).collect { |index| a[index] }
(a..b).to_a runs in O(n). Retrieving an element from an Hash is almost O(1), so the collect runs in almost O(n). There is no way to make it faster than O(n), as copying n elements always is O(n).
Hashes can have problems when they are getting too big. So I would think twice about implementing a multidimensional Array like this, if I knew my amount of data is getting big.
rows, cols = x,y # your values
grid = Array.new(rows) { Array.new(cols) }
As for accessing elements, this article is pretty good for step by step way to encapsulate an array in the way you want:
How to ruby array
You didn't state your actual goal, but maybe this can help:
require 'matrix' # bundled with Ruby
m = Matrix[
[1, 2, 3],
[4, 5, 6]
]
m.column(0) # ==> Vector[1, 4]
(and Vectors acts like arrays)
or, using a similar notation as you desire:
m.minor(0..1, 2..2) # => Matrix[[3], [6]]
Here's a 3D array case
class Array3D
def initialize(d1,d2,d3)
#data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
end
def [](x, y, z)
#data[x][y][z]
end
def []=(x, y, z, value)
#data[x][y][z] = value
end
end
You can access subsections of each array just like any other Ruby array.
#data[0..2][3..5][8..10] = 0
etc
x.transpose[6][3..8] or x[3..8].map {|r| r [6]} would give what you want.
Example:
a = [ [1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25]
]
#a[1..2][2] -> [8,13]
puts a.transpose[2][1..2].inspect # [8,13]
puts a[1..2].map {|r| r[2]}.inspect # [8,13]
I'm quite sure this can be very simple
2.0.0p247 :032 > list = Array.new(5)
=> [nil, nil, nil, nil, nil]
2.0.0p247 :033 > list.map!{ |x| x = [0] }
=> [[0], [0], [0], [0], [0]]
2.0.0p247 :034 > list[0][0]
=> 0
a = Array.new(Array.new(4))
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
a[i[j]] = 1
end
end
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
print a[i[j]] = 1 #It's not a[i][j], but a[i[j]]
end
puts "\n"
end
Here is the simple version
#one
a = [[0]*10]*10
#two
row, col = 10, 10
a = [[0]*row]*col
Here is an easy way to create a "2D" array.
2.1.1 :004 > m=Array.new(3,Array.new(3,true))
=> [[true, true, true], [true, true, true], [true, true, true]]

Resources