How to get the max of an arbitrary function in Ruby? - ruby-on-rails

Consider some function foo:
def foo(input)
input * 2
end
How to get the max value of input for some array a?
a = [3, 5, 7, 9, 6]
Something like the following (which doesn't work) should return 9:
a.max do |value|
foo(value)
end
How to do it?
Ruby 1.9.2

You need max_by , not max. http://www.ruby-doc.org/core-1.9.3/Enumerable.html#method-i-max_by
max:
Returns the object in enum with the maximum value. The first form
assumes all objects implement Comparable; the second uses the block to
return a <=> b.
a = %w(albatross dog horse)
a.max #=> "horse"
a.max {|a,b| a.length <=> b.length } #=> "albatross"
So max does take a block, but it doesn't do what you expected it to.
max_by:
Returns the object in enum that gives the maximum value from the given
block.
If no block is given, an enumerator is returned instead.
a = %w(albatross dog horse)
a.max_by {|x| x.length } #=> "albatross"

use array map: a.map{|v|foo(v)}.max

Related

Using .map function to create hashes

I have an array [5,2,6,4] and I would like to create a structure such as the first minus the second etc until the last row.
I have tried using map, but not sure how to proceed since i might need indxes.
I would like to store the result in something that looks like:
{1 => (5, 2, 3), 2 =>(2,6,-4), 3 => (6,4,2)}
So an array of x should return x-1 hashes.
Anybody knows how to do? should be a simple one.
Thank you.
First, you want to work with the array elements in pairs: 5,2, 2,6, ... That means you want to use each_cons:
a.each_cons(2) { |(e1, e2)| ... }
Then you'll want the index to get the 1, 2, ... hash keys; that suggests throwing a Enumerator#with_index into the mix:
a.each_cons(2).with_index { |(e1, e2), i| ... }
Then you can use with_object to get the final piece (the hash) into play:
a.each_cons(2).with_index.with_object({}) { |((e1, e2), i), h| h[i + 1] = [e1, e2, e1 - e2] }
If you think all the parentheses in the block's arguments are too noisy then you can do it in steps rather than a single one-liner.
You can use each_index:
a = [5, 2, 6, 4]
h = {}
a[0..-2].each_index { |i| h[i+1] = [a[i], a[i+1], a[i] - a[i+1]] }
h
=> {1=>[5, 2, 3], 2=>[2, 6, -4], 3=>[6, 4, 2]}
Try to use
each_with_index
Suppose you have an array:
arr = [3,[2,3],4,5]
And you want to covert with hash(key-value pair). 'Key' denotes an index of an array and 'value' denotes value of an array. Take a blank hash and iterate with each_with_index and pushed into the hash and finally print the hash.
Try this:
hash={}
arr.each_with_index do |val, index|
hash[index]=val
end
p hash
Its output will be:
{0=>3, 1=>[2, 3], 2=>4, 3=>5}
If you want that index always starts with 1 or 2 etc then use
arr.each.with_index(1) do |val, index|
hash[index] = val
end
Output will be:
{1=>3, 2=>[2, 3], 3=>4, 4=>5}

Can I pass an array and a block as a parameter?

Currently learning to create blocks and my own version of Array methods. I was wondering if I could pass an array and a block as parameters to a method. Below is the code I am working on and it should be self-explanatory in what it should do but I keep getting an error. Especially with the method call and where I pass the block.
def mapper(arr, &prc)
new_array = []
arr.length.times do |i|
new_array << prc.call(arr[i])
end
new_array
end
mapper([1,2,3,4], {|i| i * 2})
You can do it by passing the block outside the parentheses (adjacent to the method call):
p mapper([1, 2, 3, 4]) { |index| index * 2 }
# [2, 4, 6, 8]
Otherwise it'll result in a syntax error. Ruby won't know where the block is being passed.
As a side note, you can also define only the array as a needed argument, and then yield the block being passed:
def mapper(arr)
arr.size.times.map do |i|
yield(arr[i])
end
end
p mapper([1, 2, 3, 4]) { |index| index * 2 }
# [2, 4, 6, 8]

How do I find the maximum value of an object's attribute that occurs exactly twice in an array?

I'm using Ruby 2.4. If I want to find the maximum number of a numeric attribute of my model, I can do
max_num = my_objects_arr.maximum(:numeric_attr)
but how would I find the maximum number of attributes whose values occur exactly twice in my array? That is, let's say my objects array has three objects
obj1 - numeric_attr = 3
obj2 - numeric_attr = 3
obj3 - numeric_attr = 4
The maximum of the attributes above that occur exactly twice would be "3". Although "4" is the maximum of all attributes, it only occurs once in the array.
array = [1, 2, 3, 2, 3, 4]
array.group_by { |e| e } # group_by(&:itself) since 2.3
.select { |_, v| v.count == 2 }
.keys
.max
#⇒ 3
For objects and attributes:
my_objects_arr.group_by { |o| o.numeric_attr }
.select { |_, v| v.count == 2 }
.keys
.max
To get the objects themselves:
my_objects_arr.group_by { |o| o.numeric_attr }
.select { |_, v| v.count == 2 }
.max_by(&:first)
.last
Since you are using rails calculations e.g. #maximum this should work for you
my_objects_arr
.group(:numeric_attr)
.having("count(numeric_attr) = 2")
.maximum(:numeric_attr)
This will find the maximum value of numeric_attr by grouping them by the numeric_attr and selecting the numeric_attr that have exactly 2
SQL estimation
SELECT
MAX(numeric_attr)
FROM
[SOME TABLE]
GROUP BY
numeric_attr
HAVING
COUNT(numeric_attr) = 2
arr = [1, 2, 3, 2, 3, 4]
arr.each_with_object(Hash.new(0)) {|n,h| h[n] += 1}.select {|_,nbr| nbr == 2}.keys.max
#=> 3
This uses the form of Hash::new that creates a hash h with a default value of zero. That means that if h does not have a key k, h[k] returns zero (without altering the hash). This refers to the method Hash#[], not to be confused with Hash#[]=.

Convert array to single object if array is a single object

Really simple question.
Is there a method to do the following?
["a"] => "a"
[1] => 1
[1,"a"] => [1, "a"]
i.e. if an array is a single object, return the object, otherwise return the array.
Without doing something ugly like
array.length == 1 ? array[0] : array
basically you should stick to what you wrote - it's simple and does what it should.
of course you may always monkey patch the Array definition... (unrecommended, but it does what you expect)
class Array
def first_or_array
length > 1 ? self : self[0]
end
end
[1].first_or_array # 1
[1, 2].first_or_array # [1, 2]

Getting the number of same elements in both arrays in Ruby

I need to count the number of values that both arrays have.
def process_2arrays(arr1, arr2)
length1 = arr1.count
length2 = arr2.count
arr3 = []
i = 0
while length1 >= i do
ci = arr1[i]
if arr2.include?(ci)
arr3 << ci
damn = arr3.count
i = i + 1
end
return [(damn), (2), (3), (4)]
end
end
When I pass the values to the function it returns [nil, 2, 3, 4]
Whats the problem here?
To find elements that exist in both arrays, use the set intersection method &.
http://ruby-doc.org/core-2.2.0/Array.html#method-i-26
def count_same_elements(a1,a2)
(array1 & array2).length
end
Example
count_same_elements([1,2,3,4],[2,3,4,5])
=> 3
damn is initialized within a do .. end block, specifically the while block. Therefore, its value will live within that scope, and when you call the variable outside the block its value is nil.
If you want to preserve the value, you must initialize the variable to nil outside the block.
i = 0
damn = nil
...
As a side note, your code is lacking the most basic Ruby standards. In Ruby you generally use an iterator, not the while. Moreover, you don't use the return at the end of a method.
This is how you would write your method in Ruby using the iterators and taking advantage of some methods from the core library.
def process_2arrays(arr1, arr2)
arr3 = arr1.select { |e| arr2.include?(e) }
[arr3.size, 2, 3, 4]
end
Changing completely approach, you can use
def process_2arrays(arr1, arr2)
(arr1 & arr2).size
end

Resources