Cool tricks and expressive snippets with ruby collections/enumerables [closed] - ruby

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
What are your favorite code snippets with ruby collections? Preferably they should be discovery for you, be expressive, readable and introduce some fun in your coding practice.
Pattern-matching in arrays (for local variables and parameters):
(a, b), c = [[:a, :b], :c]
[a,b,c]
=> [:a, :b, :c]
(a,), = [[:a]]
a
=> :a
Assigning from non-arrays to multiple variables:
abc, a, b =* "abc".match(/(a)(b)./)
=> ["abc", "a", "b"]
nil1, =* "abc".match(/xyz/)
=> []
Initialize array elements with the same expression:
5.times.map { 1 }
=> [1,1,1,1]
Array.new(5) { 1 }
=> [1,1,1,1,1]
Initialize array with the same value:
[2]*5
=>[2,2,2,2,2]
Array.new 5, 2
=>[2,2,2,2,2]
Sum elements of an array:
[1,2,3].reduce(0, &:+)
=> 6
Find all indices that match condition:
a.each_with_index.find_all { |e, i| some_predicate(e) }.map(&:last)
Alternate CSS classes:
(1..4).zip(%w[cls1 cls2].cycle)
=> [[1, "cls1"], [2, "cls2"], [3, "cls1"], [4, "cls2"]]
Unzipping:
keys, values = {a: 1, b: 2}.to_a.transpose
keys
=> [:a, :b]
Exploring boolean member methods of a string:
"".methods.sort.grep(/\?/)
Exploring string-specific methods:
"".methods.sort - [].methods

Lazy Fibonacci series with memoization, taken from Neeraj Singh:
fibs = { 0 => 0, 1 => 1 }.tap do |fibs|
fibs.default_proc = ->(fibs, n) { fibs[n] = fibs[n-1] + fibs[n-2] }
end
fibs.take(10).map(&:last).each(&method(:puts))
An implementation of Counting Sort:
module Enumerable
def counting_sort(k)
reduce(Array.new(k+1, 0)) {|counting, n| counting.tap { counting[n] += 1 }}.
map.with_index {|count, n| [n] * count }.flatten
end
end
An implementation of sum aka prefix sum:
module Enumerable
def scan(initial=nil, sym=nil, &block)
args = if initial then [initial] else [] end
unless block_given?
args, sym, initial = [], initial, first unless sym
block = ->(acc, el) { acc.send(sym, el) }
end
[initial || first].tap {|res|
reduce(*args) {|acc, el|
block.(acc, el).tap {|e|
res << e
}
}
}
end
end
Here, I experimented with having Hash#each yield KeyValuePairs instead of two-element Arrays. It's quite surprising, how much code still works, after doing such a brutal monkey-patch. Yay, duck typing!
class Hash
KeyValuePair = Struct.new(:key, :value) do
def to_ary
return key, value
end
end
old_each = instance_method(:each)
define_method(:each) do |&blk|
old_each.bind(self).() do |k, v|
blk.(KeyValuePair.new(k, v))
end
end
end
Something I have been playing around with is making Enumerable#=== perform recursive structural pattern matching. I have no idea if this is in any way useful. I don't even know if it actually works.
module Enumerable
def ===(other)
all? {|el|
next true if el.nil?
begin
other.any? {|other_el| el === other_el }
rescue NoMethodError => e
raise unless e.message =~ /any\?/
el === other
end
}
end
end
Another thing I toyed around with recently was re-implementing all methods in Enumerable, but using reduce instead of each as the basis. In this case, I know it doesn't actually work properly.
module Enumerable
def all?
return reduce(true) {|res, el| break false unless res; res && el } unless block_given?
reduce(true) {|res, el| break false unless res; res && yield(el) }
end
def any?
return reduce(false) {|res, el| break true if res || el } unless block_given?
reduce(false) {|res, el| break true if res || yield(el) }
end
def collect
reduce([]) {|res, el| res << yield(el) }
end
alias_method :map, :collect
def count(item=undefined = Object.new)
return reduce(0) {|res, el| res + 1 if el == item } unless undefined.equal?(item)
unless block_given?
return size if respond_to? :size
return reduce(0) {|res, el| res + 1 }
end
reduce(0) {|res, el| res + 1 if yield el }
end
def detect(ifnone=nil)
reduce(ifnone) {|res, el| if yield el then el end unless res }
end
alias_method :find, :detect
def drop(n=1)
reduce([]) {|res, el| res.tap { res << el unless n -= 1 >= 0 }}
end
def drop_while
reduce([]) {|res, el| res.tap { res << el unless yield el }}
end
def each
tap { reduce(nil) {|_, el| yield el }}
end
def each_with_index
tap { reduce(-1) {|i, el| (i+1).tap {|i| yield el, i }}}
end
def find_all
reduce([]) {|res, el| res.tap {|res| res << el if yield el }}
end
alias_method :select, :find_all
def find_index(item=undefined = Object.new)
return reduce(-1) {|res, el| break res + 1 if el == item } unless undefined.equals?(item)
reduce(-1) {|res, el| break res + 1 if yield el }
end
def grep(pattern)
return reduce([]) {|res, el| res.tap {|res| res << el if pattern === el }} unless block_given?
reduce([]) {|res, el| res.tap {|res| res << yield(el) if pattern === el }}
end
def group_by
reduce(Hash.new {|hsh, key| hsh[key] = [] }) {|res, el| res.tap { res[yield el] = el }}
end
def include?(obj)
reduce(false) {|res, el| break true if res || el == obj }
end
def reject
reduce([]) {|res, el| res.tap {|res| res << el unless yield el }}
end
end

Initialize multiple values from an array:
a = [1,2,3]
b, *c = a
assert_equal [b, c], [1, [2,3]]
d, = a
assert_equal d, a[0]

My own are:
Initialize array elements with same expression:
5.times.map { some_expression }
Initialize array with same value:
[value]*5
Sum elements of an array:
[1,2,3].reduce(0, &:+)
Find all indices that match condition:
a.each_with_index.find_all { |e, i| some_predicate(e) }.map(&:last)

Not really snippets, but I like these generic constructions (I show only how to use them, the implementation is easily found on the web).
Conversion Array -> Hash (to_hash or mash, the idea is the same, see Facets implementation):
>> [1, 2, 3].mash { |k| [k, 2*k] }
=> {1=>2, 2=>4, 3=>6}
Map + select/detect: You want to do a map and get only the first result (so a map { ... }.first would inefficient):
>> [1, 2, 3].map_select { |k| 2*k if k > 1 }
=> [4, 6]
>> [1, 2, 3].map_detect { |k| 2*k if k > 1 }
=> 4
Lazy iterations (lazy_map, lazy_select, ...). Example:
>> 1.upto(1e100).lazy_map { |x| 2 *x }.first(5)
=> [2, 4, 6, 8, 10]

Count the number of items that meet either one condition or another:
items.count do |item|
next true unless first_test?(item)
next true unless second_test?(item)
false
end
count means you don't have to do i = 0 and i += 1.
next means that you can finish that iteration of the block and still supply the answer, rather than hanging around until the end.
(If you wanted, you could replace the last two lines of the block with the single line ! second_test?(item), but that'd make it look messier)

Exploring boolean member methods of a string:
"".methods.sort.grep(/\?/)
Exploring string-specific methods:
"".methods.sort - [].methods

Related

How to turn a ruby hash so that the value becomes a key that points to a group of old keys?

I have a hash like this:
t={"4nM"=>"Triangle", "I40"=>"Triangle", "123"=>"Square"}
And I want to turn it into a hash like:
{"Triangle" => ["4nM", "I40"], "Square" => ["123"]}
What is the best way to do this?
I start with group_by but then the code gets to be a bit convoluted....
This is what I did:
t.group_by { |k, v| v }.map { |type, group| {type => group.flatten.reject { |x| x == type } } }
h = { "4nM"=>"Triangle", "I40"=>"Triangle", "123"=>"Square" }
h.each_with_object({}) { |(k,v),h| (h[v] ||= []) << k }
#=> {"Triangle"=>["4nM", "I40"], "Square"=>["123"]}
The expression
(h[v] ||= []) << k
expands to
(h[v] = h[v] || []) << k
If h has a key v, h[k] will be truthy, so the expression above reduces to
(h[v] = h[v]) << k
and then
h[v] << k
If h does not have a key v, h[k] #=> nil, so the expression above reduces to
(h[v] = []) << k
resulting in
h[v] #=> [k]
Alternatively, we could write
h.each_with_object(Hash.new { |h,k| h[k] = [] }) { |(k,v),h| h[v] << k }
#=> {"Triangle"=>["4nM", "I40"], "Square"=>["123"]}
See Hash::new for an explanation of the use of a block for returning the default values of keys that are not present in the hash.
This is the shortest I could write :
t.group_by(&:last).map{|k,v|[k,v.map(&:first)]}.to_h
Still 4 characters longer than #Cary Swoveland's answer.
Note that in Rails, Hash#transform_values makes it a bit easier :
t.group_by{|_,v| v }.transform_values{|v| v.map(&:first) }
You can cut it down a little bit by doing this
t.group_by {|k,v| v}.map{|k,v| {k => v.map(&:first)}}
but your original implementation was already pretty concise.
t={"4nM"=>"Triangle", "I40"=>"Triangle", "123"=>"Square"}
h = Hash.new{[]}
t.each{|k,v| h[v] <<= k}

Ruby Program to solve Circular Primes below number x

I'm working on project Euler #35. I am getting the wrong number returned and I can't find where I have done wrong!
def is_prime?(num)
(2..Math.sqrt(num)).each { |i| return false if num % i == 0}
true
end
def is_circular?(num)
len = num.to_s.length
return true if len == 1
(len - 1).times do
new_n = cycle(num)
break unless is_prime?(new_n)
end
end
def cycle(num)
ary = num.to_s.split("")
return ary.rotate!.join.to_i
end
def how_many
circulars = []
(2..1000000).each do |num|
if is_prime?(num) && is_circular?(num)
circulars << num
end
end
p circulars.count
end
how_many #=> 14426
The returned number is '14426'. I am only returning the circular primes, supposedly the correct answer is '55'
I have edited your code with few fixes in Ruby way. Your mistake was including corect set of [a, b, c] three times to count, instead of counting them as a one circular prime number. Your answer was correct, while 55 is the number of unique sets.
require 'prime'
class Euler35
def is_circular?(num)
circulars_for(num).all?{ |el| ::Prime.instance.prime?(el) }
end
def circulars_for(a)
a.to_s.split("").length.times.map{|el| a.to_s.split("").rotate(el).join.to_i }
end
def how_many
circulars = []
::Prime.each(1_000_000) do |num|
continue if circulars.include?(num)
if is_circular?(num)
circulars << circulars_for(num)
end
end
circulars.count
end
end
puts Euler35.new.how_many # => 55

Turning a method into an enumerable method

I rewrote the map method:
def my_map(input, &block)
mod_input = []
x = -1
while x < input.length - 1
x = x + 1
if block == nil
return input
break
end
mod_input.push(block.call(input[x]))
end
return mod_input
end
I need to call this code as I would call map or reverse. Does anyone know the syntax for that?
Are you asking how you put a method into a module? That's trivial:
module Enumerable
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
if block == nil
return self
break
end
mod_input.push(block.call(self[x]))
end
return mod_input
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
Or are you asking how to make your method an Enumerable method? That's more involved: your method currently uses many methods that are not part of the Enumerable API. So, even if you make it a member of the Enumerable module, it won't be an Enumerable method. Enumerable methods can only use each or other Enumerable methods. You use length and [] both of which are not part of the Enumerable interface, for example, Set doesn't respond to [].
This would be a possible implementation, using the Enumerable#inject method:
module Enumerable
def my_map
return enum_for(__method__) unless block_given?
inject([]) {|res, el| res << yield(el) }
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
A less elegant implementation using each
module Enumerable
def my_map
return enum_for(__method__) unless block_given?
[].tap {|res| each {|el| res << yield(el) }}
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
Note that apart from being simply wrong, your code is very un-idiomatic. There is also dead code in there.
the break is dead code: the method returns in the line just before it, therefore the break will never be executed. You can just get rid of it.
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
if block == nil
return self
end
mod_input.push(block.call(self[x]))
end
return mod_input
end
Now that we have gotten rid of the break, we can convert the conditional into a guard-style statement modifier conditional.
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
return self if block == nil
mod_input.push(block.call(self[x]))
end
return mod_input
end
It also doesn't make sense that it is in the middle of the loop. It should be at the beginning of the method.
def my_map(&block)
return self if block == nil
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
return mod_input
end
Instead of comparing an object against nil, you should just ask it whether it is nil?: block.nil?
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
return mod_input
end
Ruby is an expression-oriented language, the value of the last expression that is evaluated in a method body is the return value of that method body, there is no need for an explicit return.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
mod_input
end
x = x + 1 is more idiomatically written x += 1.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input.push(block.call(self[x]))
end
mod_input
end
Instead of Array#push with a single argument it is more idiomatic to use Array#<<.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << block.call(self[x])
end
mod_input
end
Instead of Proc#call, you can use the .() syntactic sugar.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << block.(self[x])
end
mod_input
end
If you don't want to store, pass on or otherwise manipulate the block as an object, there is no need to capture it as a Proc. Just use block_given? and yield instead.
def my_map
return self unless block_given?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << yield(self[x])
end
mod_input
end
This one is opinionated. You could move incrementing the counter into the condition.
def my_map
return self unless block_given?
mod_input = []
x = -1
while (x += 1) < length
mod_input << yield(self[x])
end
mod_input
end
And then use the statement modifier form.
def my_map
return self unless block_given?
mod_input = []
x = -1
mod_input << yield(self[x]) while (x += 1) < length
mod_input
end
Also, your variable names could use some improvements. For example, what does mod_input even mean? All I can see is that it is what you output, so why does it even have "input" in its name? And what is x?
def my_map
return self unless block_given?
result = []
index = -1
result << yield(self[index]) while (index += 1) < length
result
end
This whole sequence of initializing a variable, then mutating the object assigned to that variable and lastly returning the object can be simplified by using the K Combinator, which is available in Ruby as Object#tap.
def my_map
return self unless block_given?
[].tap {|result|
index = -1
result << yield(self[index]) while (index += 1) < length
}
end
The entire while loop is useless. It's just re-implementing Array#each, which is a) unnecessary because Array#each already exists, and b) means that your my_map method will only work with Arrays but not other Enumerables (for example Set or Enumerator). So, let's just use each instead.
def my_map
return self unless block_given?
[].tap {|result|
each {|element|
result << yield(element)
}
}
end
Now it starts to look like Ruby code! What you had before was more like BASIC written in Ruby syntax.
This pattern of first creating a result object, then modifying that result object based on each element of a collection and in the end returning the result is very common, and it even has a fancy mathematical name: Catamorphism, although in the programming world, we usually call it fold or reduce. In Ruby, it is called Enumerable#inject.
def my_map
return self unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
That return self is strange. map is supposed to return a new object! You don't return a new object, you return the same object. Let's fix that.
def my_map
return dup unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
And actually, map is also supposed to return an Array, but you return whatever it is that you called map on.
def my_map
return to_a unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
But really, if you look at the documentation of Enumerable#map, you will find that it returns an Enumerator and not an Array when called without a block.
def my_map
return enum_for(:my_map) unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
And lastly, we can get rid of the hardcoded method name using the Kernel#__method__ method.
def my_map
return enum_for(__method__) unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
Now, that looks a lot better!
class Array
def my_map(&block)
# your code, replacing `input` with `self`
end
end
The code itself is not really idiomatic Ruby - while is very rarely used for iteration over collections, and if you don't need to pass a block somewhere else, it is generally cleaner to use block_given? instead of block.nil? (let alone block == nil), and yield input[x] instead of block.call(input[x]).

Does ruby support recursive enumerators?

I'm writing a method to merge two streams of numbers, and I've two alternative implementations:
def merge1(l1, l2)
Enumerator.new do |yielder|
h = case l1.peek <=> l2.peek
when -1 then l1.next
when +1 then l2.next
else l1.next; l2.next
end
yielder << h
yielder << merge1(l1, l2).next
end.lazy
end
def merge2(l1, l2)
Enumerator.new do |yielder|
loop do
h = case l1.peek <=> l2.peek
when -1 then l1.next
when +1 then l2.next
else l1.next; l2.next
end
yielder << h
end
end.lazy
end
puts merge2((1..Float::INFINITY).lazy.map {|x| x * 2}, (1..Float::INFINITY).lazy.map {|x| x * 3}).first(10)
But merge1 only prints "2 3" while merge2 produces the correct result.
You need to yield each item that sub-enumerator generate:
def merge1(l1, l2)
Enumerator.new do |yielder|
h = case l1.peek <=> l2.peek
when -1 then l1.next
when +1 then l2.next
else l1.next; l2.next
end
yielder << h
merge1(l1, l2).each do |h| # <----
yielder << h # <----
end # <----
end.lazy
end
Rewriting the method to use yield might improve comprehension; you can then use Object#enum_for, thereby separating concerns:
Merge two lists.
Enumerate through that merged list.
While I would discourage recursion in this case (see alternative loop approach below), here's how that would work:
def merge1(l1, l2, &block)
h = case l1.peek <=> l2.peek
when -1 then l1.next
when +1 then l2.next
else l1.next
l2.next
end
yield h
merge1(l1, l2, &block)
end
enum_for(:merge1,
(1..Float::INFINITY).lazy.map { |x| x * 2 },
(1..Float::INFINITY).lazy.map { |x| x * 3 }
).lazy.first(10)
In this case, using a straight-forward loop would be a better approach than recursion because loop automatically handles StopIteration exceptions in case one passes non-infinite lists, and because you can easily use Enumerator::new...a good reminder to use the right tool for the job:
def merge(l1, l2)
Enumerator.new do |yielder|
loop do
h = case l1.peek <=> l2.peek
when -1 then l1.next
when +1 then l2.next
else l1.next
l2.next
end
yielder << h
end
end
end
merge((1..5).lazy.map { |x| x * 2 },
(1..5).lazy.map { |x| x * 3 }
).lazy.first(10).to_a
# => [2, 3, 4, 6, 8, 9, 10]
# No need to handle `StopIteration` exceptions;
# still supports infinite ranges.

Filter arrays with bitmask or other array in Ruby

I was wondering if there was an Array method in Ruby that allows to filter an array based on another array or a bitmask.
Here is an example and a quick implementation for illustration purposes:
class Array
def filter(f)
res = []
if f.is_a? Integer
(0...self.size).each do |i|
res << self[i] unless f[i].nil? || 2**i & f == 0
end
else
(0...self.size).each do |i|
res << self[i] unless f[i].nil? || f[i] == 0
end
end
return res
end
end
Example:
%w(a b c).filter([1, 0, 1]) ==> ['a', 'c']
%w(a b c).filter(4) ==> ['c']
%w(a b c).filter([1]) ==> ['a']
Thanks!
In ruby 1.9 Fixnum#[] gives you bit values at a particular position, so it will work for both integers and arrays. I'm thinking something like this:
class Array
def filter f
select.with_index { |e,i| f[i] == 1 }
end
end
%w(a b c).filter([1, 0, 1]) #=> ['a', 'c']
%w(a b c).filter(4) #=> ['c']
%w(a b c).filter(5) #=> ['a', c']
%w(a b c).filter([1]) #=> ['a']
class Array
def filter(f)
f = f.to_s(2).split("").map(&:to_i) unless Array === f
reverse.reject.with_index{|_, i| f[-i].to_i.zero?}
end
end

Resources