Write method group_by on my own [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I need to write the method group_by by myself. This is what I have so far:
module Enumerable
def group_by(&b)
solution = {}
self.each {|key,val|
b.call(var)
solution = { key=> (val) }
}
end
end
ary = [1,2,3,4,5,6,7,8,9,10]
p ary.group_by() { |i| i%3 }
I don't get it. I hope you can help me.

module Enumerable
def group_by &b; inject({}){|h, e| (h[b.call(e)] ||= []).push(e); h} end
end
[1,2,3,4,5,6,7,8,9,10].group_by{|i| i % 3}
# => {1 => [1, 4, 7, 10], 2 => [2, 5, 8], 0 => [3, 6, 9]}

I'm not really sure how I can help apart from post a solution, but maybe some description with what you find difficult might help?
Few issues I notice:
You are using passing two arguments into the block when the array only has 1, the value
You call the block with var which doesn't exist in the current scope, maybe you meant val?
You dont check to see if anything already exists in the new solution dictionary
You overwrite the solution dictionary every time you loop over a new value in the array
Here is what I came up with:
module Enumerable
def group_by
solution = {}
each do |value|
key = yield value
if solution.key?(key)
solution[key] << value
else
solution[key] = [value]
end
end
solution
end
end
ary = [1, 2, 3, 4, 5]
p ary.group_by { |i| i % 3 }
output:
{1=>[1, 4], 2=>[2, 5], 0=>[3]}
you might want to check if a block has been given incase someone tries to use the function wrong, consider using the statement unless block_given? but maybe you can try implement this yourself.

Another solution for comparison:
module Enumerable
def group_by
{}.tap do |group|
each{ |value| (group[ yield(value) ] ||= []) << value }
end
end
end
uses tap to avoid the unsightly pattern of
thing = {}
# do stuff with thing
thing # return it
uses ||= to create the new collection array of not already present
Alternatively:
module Enumerable
def group_by
Hash.new{ |h,k| h[k]=[] }.tap do |group|
each{ |value| group[ yield(value) ] << value }
group.default = nil # remove the default_proc when done
end
end
end

From my backports gem:
module Enumerable
def group_by
return to_enum(:group_by) unless block_given?
result = {}
each do |o|
key = yield(o)
ary = result.fetch(key){ result[key] = [] }
ary << o
end
result
end
end
Contrary to all solutions presented so far, it passes RubySpec.

Related

Built in way to concatenate two Enumerators [duplicate]

This question already has answers here:
How can I make a ruby enumerator that does lazy iteration through two other enumerators?
(3 answers)
Closed 4 years ago.
Say you have:
enum1 = 1.upto(5)
enum2 = 7.upto(10)
I want:
enum_combined = enum1.some_method(enum2)
such that:
enum_combined.to_a #=> [1, 2, 3, 4, 5, 7, 8, 9, 10]
I don't see any method on the Enumerator class that would do this, but before rolling my own solution I'd like make sure I'm not missing some built-in way to do this.
To be clear: I want the returned result to be another Enumerator object, since I want the entire calculation to be lazy.
UPDATE
Per the linked duplicate, the way to achieve this is:
combined = [enum1, enum2].lazy.flat_map(&:lazy)
You can define a new enumerator, iterating through your existing enumerators. Something like:
enum = Enumerator.new { |y|
enum1.each { |e| y << e }
enum2.each { |e| y << e }
}
You can make Enumerator class extension like this:
class Enumerator
def self.concat(*enumerators)
self.new do |y|
enumerators.each do |e|
e.each {|x| y << x }
end
end
end
end
and use it like that:
enum3 = Enumerator.concat(enum1, enum2)

Return a new array modified from a block/yield in ruby

I have just started a full-stack developer course at bloc.io, and I am struggling on an assignment. I cannot seem to find the issue with my code, but I am also a bit unclear as to what the assignment may be asking for. Any guidance would be greatly appreciated. The assignment gives the following examples. I apologize for the length of this post, but I wanted to be as thorough as possible.
def return_bigger(array)
array.map do |item|
yield(item)
end
end
return_bigger([1,2,3,4]) do |item|
item + 1000
end
#=> [1001, 1002, 1003, 1004]
return_bigger(["cat", "hat", "bat"]) do |item|
item.capitalize
end
#=> ["Cat", "Hat", "Bat"]
new_each([1,2,3,4]) do |item|
p "Whatever I want! Item: #{item}"
end
def new_each(array)
0.upto(array.length - 1) do |index|
yield( array[index] )
end
end
Then describes the assignment as follows:
Define a new_map function. It should take an array as an argument and return a new array modified according to the instructions passed in as a block. Feel free to use each within the method, rather than the array indexing we used above.
The first step in re-implementing map should be to iterate over the array:
def new_map(array)
array.each do |item|
end
end
The new_map method will be quite similar to our new_each method, but rather than just performing "side effect" behavior with each element, you'll want to store the return value from each block invocation in a new array:
def new_map(array)
new_array = []
array.each do |item|
# invoke the block, and add its return value to the new array
end
end
When you've finished iterating through the old array, just return the new one from your new_map function.
From what I can comprehend, the assignment wants me to replicate the new_each method without the use of .map then store it in a "new_array" However, I am unsure what the flaw in my code is. Is there a reason that my code is not "yielding" the blocks I have defined? This is the code I have come up with:
def new_map(array)
new_array = []
array.each do |item|
yield(item)
new_array << item
end
end
new_map([1,2,3,4]) do |item|
item + 1
end
new_map(["cat", "hat", "bat"]) do |item|
item.capitalize
end
assignment:
new_map should not call map or map!
RSpec::Expectations::ExpectationNotMetError
expected: [2, 3, 4]
got: [1, 2, 3]
(compared using ==)
exercise_spec.rb:9:in `block (2 levels) in <top (required)>'
new_map should map any object
RSpec::Expectations::ExpectationNotMetError
expected: [Fixnum, String, Symbol]
got: [1, "two", :three]
(compared using ==)
exercise_spec.rb:14:in `block (2 levels) in <top (required)>'
specs:
describe "new_map" do
it "should not call map or map!" do
a = [1, 2, 3]
a.stub(:map) { '' }
a.stub(:map!) { '' }
expect( new_map(a) { |i| i + 1 } ).to eq([2, 3, 4])
end
it "should map any object" do
a = [1, "two", :three]
expect( new_map(a) { |i| i.class } ).to eq([Fixnum, String, Symbol])
end
end
Two tiny mistakes: the fact that you are stuffing your new_array with original items, rather than with the transformed items that you are getting from yield item, and (as already mentioned by Cary Swoveland in comments) not returning new_array. As it is, you are returning the last computed value, which is the result of array.each, which is array - so instead of your computed result, you are returning the original array. This is why you are receiving [1, 2, 3] when you are expecting [1+1, 2+1, 3+1].
def new_map(array)
new_array = []
array.each do |item|
yield(item)
new_array << yield(item)
end
new_array
end
new_map([1,2,3,4]) do |item|
item + 1
end
new_map(["cat", "hat", "bat"]) do |item|
item.capitalize
end

Return Enumerator without using existing iterators

How do I return Enumerator from my array wrapper without using already existing array iterators?
class MyArray
def initialize
#inner = []
end
def each
index = 0
while index < #inner.size
yield #inner[index] if block_given?
index += 1
end
end
end
I can't figure out how to avoid calling things like #inner.each at the end of the each method.
Given:
#inner = [1, 2, 3]
Code
#inner.to_enum
will return an enumerator.
enum = #inner.to_enum
enum.each{|e| p e}
# => 1, 2, 3

How do I call a method, given its name, on an element of an array?

How do I call a method, given its name, on an element of an array?
For example, I could have:
thing = "each"
I want to be able to do something like:
def do_thing(thing)
array = [object1,object2]
array[0].thing
end
so that do_thing(to_s), for example, would run object1.to_s.
You can use public_send or send. public_send only sends to public methods while send can see public and private methods.
def do_thing(thing)
array = [1,2,3]
array.public_send(thing)
end
do_thing('first')
# => 1
do_thing(:last)
# => 3
Update A more general version:
def do_thing(array, index, method, *args)
array[index].public_send(method, *args)
end
do_thing([1, 2, 3], 0, :to_s)
# => "1"
do_thing([[1,2], [3, 4]], 0, :fetch, 0)
# => 1
require 'ostruct'
o = OpenStruct.new(attribute: 'foo')
do_thing([o], 0, :attribute=, 'bar')
o.attribute == 'bar'
# => true
Object#send
thing = "each"
def do_thing(thing)
array = [1,2,3]
array.send(thing)
end
From the doc:
class Klass
def hello(*args)
"Hello " + args.join(' ')
end
end
k = Klass.new
k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
Here is an example to help you out although I don't have any idea what objects are residing inside your array:
arr = [Array.new(2,10),"abc" ]
arr.each{|i| p i.send(:length)}
#>>2
#>>3

Iterate through a part of enumerator(external iterator)?

If I want a part of an array I can use [] or split:
arr = [1,2,3,4,5]
arr[1..3]
=> [2, 3, 4]
But is there a 'general' version of []? Can I apply it to any Enumerator?
enum = arr.each
enum.xxx_method(1..3) # is equal to arr[1..3].each
Of course you can use arr[1..3] directly. But I'm seeking a general way to handle any enumerator.
If you have an enumerator, you can count on Enumerable methods drop and take:
# abstract if necessary as enum_slice(range)
enumerator.drop(2).take(3)
If that enumerator is an array you don't need to traverse it, check the method Array#lazy_slice that I asked to be added to enumerable_lazy in relation with your previous question:
require 'enumerable/lazy'
class Array
def lazy_slice(range)
Enumerator.new do |yielder|
range.each do |index|
yielder << self[index]
end
end.lazy
end
end
some_big_array = (0..10000).to_a # fake array, it won't generally be a range
p some_big_array.lazy_slice(9995..10000).map { |x| 2*x }.to_a
#=> [19990, 19992, 19994, 19996, 19998, 20000]

Resources