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)
Related
I need help understanding inheritance.
class MyArray < Array
end
a = MyArray[1, 2, 3] #=> [1, 2, 3]
b = MyArray[4, 5] #=> [4, 5]
c = a + b #=> [1, 2, 3, 4, 5]
a.class #=> MyArray
b.class #=> MyArray
c.class #=> Array
I don't understand why the result of the addition is not an instance of MyArray class.
I don't understand why my array "a" is not "MyArray" class after add.
Why should it (be a MyArray)? Concatenation operation on arrays is defined to return a new Array, so that's what happens here. https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B
If you want, you can override that operation in your class to return an instance of MyArray. Don't forget about all other similar methods.
This is also why it's a bad idea to subclass standard collections. Better to use composition over inheritance here.
Just to add a bit to Sergio's answer in terms of his comment on using composition over inheritance and the exchange in the comments.
Instead of saying MyArray is an array you can say MyArrayLike has and array.
Then you can "forward" methods that make sense to the underlying array but still add your own functionality that makes sense for your class without sub-classing array.
Ruby even has several ways to make this very easy including the Forwardable module.
class MyArrayLike
attr_reader :arr
def initialize( initial_arr )
#arr = initial_arr
end
def +(other)
result = self.class.new(arr + other.arr)
# maybe you want to do more than just concat the underlying array, if so you can do it here
result
end
def first
# for example maybe you want first to just return the first item in the underlying array.
arr.first
end
end
a = MyArrayLike.new([1,2,3])
b = MyArrayLike.new([4,5])
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4b00>
a += b
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4470>
puts a.first
# => 1
puts a.arr
# => 1
# 2
# 3
# 4
# 5
Adding MyArray to MyArray to get Array may be counterintuitive, but a method can be defined to return any class. And in the case of Array#+, which you are calling, it just happens to be defined to return an Array. That's it.
If you want it to return a MyArray, one way to do it is to define MyArray#+ as follows:
class MyArray < Array
def +other
MyArray.new(super)
end
end
(MyArray.new([1, 2, 3]) + MyArray.new([4, 5])).class # => MyArray
By the way, notice that your MyArray#initialize definition is meaningless, and hence redundant.
I am iterating over an array, and I'm wondering if there's a shorthand to refer to the receiver of #each (or #each_with_index) method from within the iteration.
self returns main.
You should be able to just reference it:
my_thing.each {|one_thing| puts my_thing }
This is pretty similar to the answer I gave here https://stackoverflow.com/a/45421168/2981429 but slightly different.
First off, you can create a scope with self bound to the array, and then execute the each in that scope:
[1].instance_exec do
# in this scope, self is the array
# thus we can use just 'each' because the self is inferred
each do |x|
# note that since 'class' is a special keyword,
# it needs to be explicitly namespaced on self
puts self.class, x
end
end
# => prints Array, 1
You can create a utility function to do this, if you want:
def bound_each(enumerable, &blk)
enumerable.instance_exec { each &blk }
end
bound_each([1]) { |x| puts self.class, x }
# prints Array, 1
You can call your each method within an Object#tap block and reference the original receiver like that.
[1, 2, 3].tap { |i| i.each { |j| p i.dup << j } }
# [1, 2, 3, 1]
# [1, 2, 3, 2]
# [1, 2, 3, 3]
#=> [1, 2, 3]
Here the receiving object is [1, 2, 3] and is passed to the block-variable i which we can use locally or in nested scopes such as each's block.
Avoid modifying the receiving object else you may end up with undesired results such as an infinite array. Using dup could allay this possibility.
This is an interesting question. As far as I know it's not possible – the closest I can come up with would be to use inject (or reduce) and explicitly pass the receiver as an argument. A bit pointless, but there might be a use-case for it that I'm not seeing:
a = [1,2,3]
a.inject(a) do |this, element|
this == a #=> true
this.include?(element) #=> true
this
end
Apart from looking a bit redundant, you have to be very sure to return this at the end of each iteration, as the return value will become this in the next iteration. For that reason (and the fact that you could just reference your collection in an each block, as in David's answer) I don't recommend using this.
Edit - as Simple Lime pointed out in the comments – I missed the obvious Enumerator#with_object, which has the same (rather pointless) effect, but without the drawback of having to return this at the end of each iteration. For example:
a = [1,2,3]
a.map.with_object(a) do |element, this|
this == a #=> true, for each iteration
end
I still don't recommend that you use this though.
What is the idiomatic Ruby way to write this code?
Given an array, I would like to iterate through each element of that array, but skip the first one. I want to do this without allocating a new array.
Here are two ways I've come up with, but neither feels particularly elegant.
This works but seems way too verbose:
arr.each_with_index do |elem, i|
next if i.zero? # skip the first
...
end
This works but allocates a new array:
arr[1..-1].each { ... }
Edit/clarification: I'd like to avoid allocating a second array. Originally I said I wanted to avoid "copying" the array, which was confusing.
Using the internal enumerator is certainly more intuitive, and you can do this fairly elegantly like so:
class Array
def each_after(n)
each_with_index do |elem, i|
yield elem if i >= n
end
end
end
And now:
arr.each_after(1) do |elem|
...
end
I want to do this without creating a copy of the array.
1) Internal iterator:
arr = [1, 2, 3]
start_index = 1
(start_index...arr.size).each do |i|
puts arr[i]
end
--output:--
2
3
2) External iterator:
arr = [1, 2, 3]
e = arr.each
e.next
loop do
puts e.next
end
--output:--
2
3
OK, maybe this is bad form to answer my own question. But I've been racking my brain on this and poring over the Enumerable docs, and I think I've found a good solution:
arr.lazy.drop(1).each { ... }
Here's proof that it works :-)
>> [1,2,3].lazy.drop(1).each { |e| puts e }
2
3
Concise: yes. Idiomatic Ruby… maybe? What do you think?
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.
I am new to Ruby, is there a way to yield values from Ruby functions? If yes, how? If not, what are my options to write lazy code?
Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.
The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:
def eternal_sequence():
i = 0
while True:
yield i
i += 1
is this:
def eternal_sequence
Enumerator.new do |enum|
i = 0
while true
enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
i +=1
end
end
end
You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.
I've seen Fibers used in that way, look at an example from this article:
fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x,y = y,x+y
end
end
20.times { puts fib.resume }
If you are looking to lazily generate values, #Chuck's answer is the correct one.
If you are looking to lazily iterate over a collection, Ruby 2.0 introduced the new .lazy enumerator.
range = 1..Float::INFINITY
puts range.map { |x| x+1 }.first(10) # infinite loop
puts range.lazy.map { |x| x+1 }.first(10) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Ruby supports generators out of the box using Enumerable::Generator:
require 'generator'
# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])
while g.next?
puts g.next
end
# Generator from a block
g = Generator.new { |g|
for i in 'A'..'C'
g.yield i
end
g.yield 'Z'
}
# The same result as above
while g.next?
puts g.next
end
https://ruby-doc.org/stdlib-1.8.7/libdoc/generator/rdoc/Generator.html
Class Enumerator and its method next behave similar
https://docs.ruby-lang.org/en/3.1/Enumerator.html#method-i-next
range = 1..Float::INFINITY
enumerator = range.each
puts enumerator.class # => Enumerator
puts enumerator.next # => 1
puts enumerator.next # => 2
puts enumerator.next # => 3