Create own Array#map like method - ruby

I want to create an instance method like Array#my_map and that method should behavior of the original Array#map method.
I want to same output from new method as below:
arr = [1, 2, 3, 4]
arr.new_map_method do |x|
x + 1
end # => [2, 3, 4, 5]
arr.new_map_method(&:to_s) # => ["1", "2", "3", "4"]

The easiest way of creating a method with the same behavior of a different method is a simple alias:
class Array
alias_method :new_map_method, :map
end
If, for whatever strange reason, you don't want to use map, you can use inject instead:
class Array
def new_map_method
return enum_for(__callee__) unless block_given?
inject([]) {|acc, el| acc << yield(el) }
end
end

Thanks guys! I also made one solution for my qeestion
class Array
def my_map(&block)
result = []
each do |element|
result << block.call(element)
end
result
end
end
function calling
[1,2,3].my_map(&:to_s)
output => ["1", "2", "3"]
[1, 2, 3, 4, 5].my_map do |x|
x
end
output => [1, 2, 3, 4, 5]

Related

How to create a function in Ruby that take a average of an array?

I tried something like this:
def average_array_float(&array)
array.inject{ |sum, el| sum + el }.to_f / array.size
end
no success
array = [1, 2, 3]
def even_numbers(array)
array.select { |num| num.even? }
end
p array.even_numbers
reply:
$ bundle exec ruby main.rb
Traceback (most recent call last):
main.rb:7:in `<main>': private method `even_numbers' called for [1, 2, 3]:Array (NoMethodError)
exit status 1
what i am doing wrong?
You have to pass the array to the method:
def even_numbers(array)
array.select { |num| num.even? }
end
array = [1, 2, 3, 4, 5, 6]
even_numbers(array)
#=> [2, 4, 6]
The NoMethodError in your example happens because if you define a method on the top-level, it becomes a private method of Object:
Object.private_methods
#=> [:initialize, :inherited, :method_added, :method_removed, :method_undefined,
# :remove_const, :initialize_copy, :initialize_clone, :using, :public,
# :ruby2_keywords, :protected, :private, :included, :extended, :prepended,
# :even_numbers, :sprintf, :format, ...]
# ^^^^^^^^^^^^^
And since array is an Object, it can access that method (privately).
If you really wanted to add the method to Array, you could open the corresponding class:
class Array
def even_numbers
select { |num| num.even? }
end
end
Which gives you:
[1, 2, 3, 4, 5, 6].even_numbers
#=> [2, 4, 6]
However, although this works, it's not advised to alter objects that are not your own, let alone Ruby's core classes.
Regarding your other method, you could use sum and fdiv:
def average(array)
array.sum.fdiv(array.size)
end
average([1, 2, 4])
#=> 2.3333333333333335
Or quo if you prefer a precise result:
def average(array)
array.sum.quo(array.size)
end
average([1, 2, 4])
#=> (7/3)

How to pass arguments directy to a class without calling new method?

In Ruby a Hash can be created by:
Hash(a: 5, b: 6)
An Array can be created like this:
Array(100)
Sets can be created with the code:
require 'set'
Set[1,2,3]
So, how can I define a class that can accept arguments without calling the initialize method?
So, how can I define a class that can accept arguments without calling the initialize method?
You can't. In your examples, Hash and Array are actually methods.
And example with Set uses Set::[], naturally. And so it's not any different from any other class method that returns you instances of that class. For instance, User::create (or what-have-you).
In Ruby a Hash can be created by:
Hash(a: 5, b: 6)
Hash() is actually a method of the Kernel module:
p defined?(Hash()) # => "method"
p defined?(Kernel.Hash()) # => "method"
But without parentheses, Hash, Array, String, etc. all are just classes:
defined?(Hash) # => "constant"
defined?(Array) # => "constant"
In Ruby 2.6.3, the same goes for Arrays(), Complex(), Float(), Hash(), Integer(), Rational(), String(), and URI() - they all are methods.
But Set is a class:
require 'set'
p defined?(Set) # => "constant"
p set = Set[1,2,3] # => #<Set: {1, 2, 3}>
p set.to_a # => [1, 2, 3]
So, Set[1,2,3] is actually calling the [] method of Set. It looks kind of like this:
class Something
def initialize(*a)
#hash = {}
a.each { |v| #hash.store(v, nil) }
end
def self.[](*a) new(*a) end
define_method(:to_a) { #hash.keys }
define_method(:inspect) { "#<#{self.class}: {#{#hash.keys.to_s[1..-2]}}>" }
alias :to_s :inspect
end
p defined?(Something) # => "constant"
p set = Something[1,2,3] # => #<Something: {1, 2, 3}>
p set1 = Something[[1, 2, 3], 2, 2, 3, 4, {4 => :a}, 5] # => #<Something: {[1, 2, 3], 2, 3, 4, {4=>:a}, 5}>
p set.to_a # => [1, 2, 3]
p set1.to_a # => [[1, 2, 3], 2, 3, 4, [4, 4], 5]
Back to the question:
So, how can I define a class that can accept arguments without calling
the initialize method?
I don't think it's possible!

How to get reference to object you're calling in method?

I am trying to create a method for objects that I create. In this case it's an extension of the Array class. The method below, my_uniq, works. When I call puts [1, 1, 2, 6, 8, 8, 9].my_uniq.to_s it outputs to [1, 2, 6, 8].
In a similar manner, in median, I'm trying to get the reference of the object itself and then manipulate that data. So far I can only think of using the map function to assign a variable arr as an array to manipulate that data from.
Is there any method that you can call that gets the reference to what you're trying to manipulate? Example pseudo-code that I could replace arr = map {|n| n} with something like: arr = self.
class Array
def my_uniq
hash = {}
each do |num|
hash[num] = 0;
end
hash.keys
end
end
class Array
def median
arr = map {|n| n}
puts arr.to_s
end
end
Thanks in advance!
dup
class Array
def new_self
dup
end
def plus_one
arr = dup
arr.map! { |i| i + 1 }
end
def plus_one!
arr = self
arr.map! { |i| i + 1 }
end
end
array = [1, 3, 5]
array.new_self # => [1, 3, 5]
array.plus_one # => [2, 4, 6]
array # => [1, 3, 5]
array.plus_one! # => [2, 4, 6]
array # => [2, 4, 6]
dup makes a copy of the object, making it a safer choice if you need to manipulate data without mutating the original object. You could use self i.e. arr = self, but anything you do that changes arr will also change the value of self. It's a good idea to just use dup.
If you do want to manipulate and change the original object, then you can use self instead of dup, but you should make it a "bang" ! method. It is a convention in ruby to put a bang ! at the end of a method name if it mutates the receiving object. This is particularly important if other developers might use your code. Most Ruby developers would be very surprised if a non-bang method mutated the receiving object.
class Array
def median
arr = self
puts arr.to_s
end
end
[1,2,3].median # => [1,2,3]

How to use &proc argument inside method

Array#max_by returns only a single value, but I want to have all values that have the max value.
hashes = [{a: 1, b:2}, {a:2, b:3}, {a:1, b:3}]
max = hashes.map{|h| h[:b]}.max
hashes.select{|h| h[:b] == max}
# => [{a: 2, b: 3}, {a: 1, b: 3}]
This code works fine, and I want to add it to Array class.
class Array
def max_values_by(&proc)
max = map(&proc).max
# I don't know how to use `select` here.
end
end
How to access the value of the &proc argument?
Use the proc in the block passed to select by calling it with call:
class Array
def max_values_by(&proc)
max = map(&proc).max
select { |h| proc.call(h) == max }
end
end
hashes.max_values_by { |h| h[:b] }
=> [{a: 2, b: 3}, {a: 1, b: 3}]
or with yield, which gives identical results:
def max_values_by(&proc)
max = map(&proc).max
select { |h| yield(h) == max }
end
Although proc.call is a little longer than yield, I prefer it in this case because it makes it clearer that the same block is being used in two places in the method, and because it's weird to use both the implicit block passing of yield and the explicit passing of &proc in the same method.
#DaveSchweisguth suggests a great implementation using select, like you requested. Another way of achieving the same result is by using group_by, like this:
>> hashes.group_by{|h| h[:b]}.max.last
=> [{:a=>2, :b=>3}, {:a=>1, :b=>3}]
or monkey-patched into Array as:
class Array
def max_values_by(&proc)
group_by(&proc).max.last
end
end

Differences between [1,2,3].to_enum and [1,2,3].enum_for in Ruby

In Ruby I'm trying to understand between the to_enum and enum_for methods. Before I my question, I've provided some sample code and two examples to help w/ context.
Sample code:
# replicates group_by method on Array class
class Array
def group_by2(&input_block)
return self.enum_for(:group_by2) unless block_given?
hash = Hash.new {|h, k| h[k] = [] }
self.each { |e| hash[ input_block.call(e) ] << e }
hash
end
end
Example # 1:
irb (main)> puts [1,2,3].group_by2.inspect
=> #<Enumerator: [1, 2, 3]:group_by2>
In example #1: Calling group_by on the array [1,2,3], without passing in a block, returns an enumerator generated with the command self.enum_for(:group_by_2).
Example #2
irb (main)> puts [1,2,3].to_enum.inspect
=> #<Enumerator: [1, 2, 3]:each>
In example #2, the enumerator is generated by calling the to_enum method on the array [1,2,3]
Question:
Do the enumerators generates in examples 1 and 2, behave differently in any way? I can see from the inspected outputs that they show slightly different labels, but I can find any difference in the enumerators' behavior.
# Output for example #1
#<Enumerator: [1, 2, 3]:each> # label reads ":each"
# Output for example #2
#<Enumerator: [1, 2, 3]:group_by2> # label reads ":group_by2"
p [1, 2, 3].to_enum
p [1, 2, 3].enum_for
--output:--
#<Enumerator: [1, 2, 3]:each>
#<Enumerator: [1, 2, 3]:each>
From the docs:
to_enum
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
...
enum_for
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
ruby is a language that often has method names that are synonyms.
Followup question:
Does the symbol in the command [1,2,3].to_enum(:foo) serve a purpose,
other than replacing :each with :foo in the output?
Yes. By default, ruby hooks up the enumerator to the receiver's each() method. Some classes do not have an each() method, for instance String:
str = "hello\world"
e = str.to_enum
puts e.next
--output:--
1.rb:3:in `next': undefined method `each' for "helloworld":String (NoMethodError)
from 1.rb:3:in `<main>
to_enum() allows you to specify the method you would like the enumerator to use:
str = "hello\nworld"
e = str.to_enum(:each_line)
puts e.next
--output:--
hello
Now, suppose you have the array [1, 2, 3], and you want to to create an enumerator for your array. An array has an each() method, but instead of creating an enumerator with each(), which will return each of the elements in the array, then end; you want to create an enumerator that starts over from the beginning of the array once it reaches the end?
e = [1, 2, 3].to_enum(:cycle)
10.times do
puts e.next()
end
--output:--
1
2
3
1
2
3
1
2
3
1

Resources