Rails Ruby - How to create loop based on counted objects? - ruby-on-rails

I have to do some very repetitive calculations based on how many objects there are.
Example there are 4 objects.
Then I have to do these calculations:
1+2
1+3
1+4
2+1
2+3
2+4
3+1
3+2
3+4
4+1
4+2
4+3
1+3+2
1+4+3
1+2+4
3+2+1
3+4+2
3+2+4
4+2+1
4+3+1
4+2+3
1+2+3+4
How to do this in a non repetitive way of calculation all possibilities ?
I want to calculate all possibilities expect that a object my not appear twice.

objs = [1, 2, 3, 4]
(1..objs.size).map {|i| objs.permutation(i).map {|o| o.reduce(:+) } }.flatten(1)
# => [
1, # 1
2, # 2
3, # 3
4, # 4
3, # 1+2
4, # 1+3
5, # 1+4
3, # 2+1
5, # 2+3
6, # 2+4
...
10, # 1+2+3+4
...
10 # 4+3+2+1
]

You can look at Array.permutations
When invoked with a block, yield all permutations of length n of the elements of ary, then return the array itself. If n is not specified, yield all permutations of all elements. The implementation makes no guarantees about the order in which the permutations are yielded.

Related

what happens when *args is passed to yield in ruby

what happens when *args passed to yield in ruby, in capture_helper.rb of rails I saw a statement where *args is passed to yield statement, what actually happens when we do so.
buffer = with_output_buffer { value = yield(*args) }
where first parameter is builder object and second parameter is the block passed
With the * operator (splat operator) prefixing a variable (which must be an array or hash), the values of the array are extracted:
ary = [1, 2, 3]
def foo(a, b, c)
a + b + c
end
foo(ary)
# => ArgumentError: wrong number of arguments (given 1, expected 3)
foo(*ary)
# 6
It's just the same with yield, except that the values are passed to the block:
def bar
ary2 = [5, 6]
yield(*ary2)
end
bar do |x, y|
puts x + y
end
# 11
Splats have many uses in ruby. When you use a splat in a method call it turns an array into a list of arguments.
def test(*args)
args
end
Consider these examples:
a = [1,2,3]
test(a)
# => [[1, 2, 3]]
test(1,2,3)
# => [1, 2, 3]
test(*a)
# => [1, 2, 3]
In the first example the array is treated as the first argument so the result is an array in an array. While *[1,2,3] de-structures the array so that we get the desired result.
This makes it extremely useful for calling methods that take a variable number of arguments. yield works just like any other method in this regard, as using a splat will de-structure the array in args and call the passed block with a list of arguments.

Options for processing X elements of an array at a time using Ruby

We want to process 1000 elements of an array at a time. What are the options for doing this in Ruby?
Obviously, one approach is to do a simple loop and slice 1000 elements from the array until no more elements remain, but was curious if there are other more elegant options.
This is for a Rails app (RoR 3.2.12).
In Rails you can use in_groups_of:
array = [1,2,3,4,5,6,7,8,9,10]
array.in_groups_of(3, false).each do |group|
puts group
end
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
We wound up using each_slice:
array = [1,2,3,4,5,6,7,8,9,10]
array.each_slice(3) do |group|
puts group
end

Ruby Enumerator-based lazy flatten method

Michael Harrison has a great post on lazy enumerators in Ruby, providing an implementation of lazy_select and lazy_map. I am wondering whether the following implementation of lazy_flatten should have special processing for anything other than Enumerator and Enumerable types.
class Enumerator
def lazy_flatten
Enumerator.new do |yielder|
self.each do |value|
if value.kind_of? Enumerator
value.lazy_flatten.each do |v|
yielder.yield v
end
elsif value.kind_of? Enumerable
value.flatten.each do |v|
yielder.yield v
end
else
yielder.yield value
end
end
end
end
end
This doesn't seem lazy to me, as you are still performing old (non-lazy) flatten beneath.
Enumerator is Enumerable, so I think you don't need to handle it separately.
I would expect lazy_flatten to be method on Enumerable.
Here's how I would implement it:
module Enumerable
def lazy_flatten
Enumerator.new do |yielder|
each do |element|
if element.is_a? Enumerable
element.lazy_flatten.each do |e|
yielder.yield(e)
end
else
yielder.yield(element)
end
end
end
end
end
Note that in Ruby 2.0+, you don't need to do this, you can just use Enumerable#lazy, which returns an Enumerator::Lazy.
For reasons that aren't clear to me, Lazy doesn't have flatten, but it has flat_map, so in principle you could just use flat_map with the identity function.
module Enumerable
def lazy_flatten
self.lazy.flat_map { |x| x }
end
end
Lazy#flat_map mostly takes care of decomposing any decomposable elements, but not quite -- from the docs:
A value x returned by block is decomposed if either of the following conditions is true:
x responds to both each and force, which means that x is a lazy enumerator.
x is an array or responds to to_ary.
Note that to_ary is not a method on Enumerable, presumably to discourage implicit conversions from infinite sequences to arrays. This means, for instance, that if you try to lazy_flatten something that contains a Set or a Range with the above code, it (arguaby, see below) won't work:
a = [[1, 2, 3], Set[4, 5], 6, 7..8]
# => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8]
f = a.lazy_flatten
# => #<Enumerator::Lazy: #<Enumerator::Lazy: [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8]>:flat_map>
f.to_a
# => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8]
However, this is the same as the behavior of Array#flatten:
a.flatten
# => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8]
(Although Array#flatten will not detect and decompose lazy enumerators, and Lazy#flat_map will.)
Whereas the OP's code or the code in Mladen Jablanović's answer will decompose the Set and the Range:
f = a.lazy_flatten # (M.J.'s code)
# => #<Enumerator: #<Enumerator::Generator:0x00007fd819c166c0>:each>
f.to_a
# => [1, 2, 3, 4, 5, 6, 7, 8]
That code, however, will also iterate infinitely if passed something that includes an infinite sequence:
a = [[1, 2, 3], Set[4, 5], 6, 7..8, 9..Float::INFINITY]
# => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8, 9..Infinity]
f = a.lazy_flatten # (M.J.'s code)
# => #<Enumerator: #<Enumerator::Generator:0x00007fd819a73d18>:each>
f.to_a
# => spins at 100% CPU for a while and eventually runs out of memory
If you consider that a feature, rather than a bug, one approach would be to modify the flat_map-based implementation to convert any enumerables it finds to lazy ones:
module Enumerable
def lazy_flatten
self.lazy.flat_map do |x|
x.respond_to?(:lazy) ? x.lazy : x
end
end
end
This works even for nested lazy enumerables, as Lazy#lazy is smart enough to return itself.

Retrieve x random elements from an array

I am struggling to write a clean method which when passed an array of strings and x returns a randomised list of array elements totalling x, eg.
def getrandomarrayelements(thearray, howmany)
return [something]
end
Yes I should submit my existing code, which whilst works is not good, it's 8 lines long and I have a feeling it can be done in one?!
In ruby 1.9:
irb(main):001:0> [1,2,3,4,5].sample(3)
=> [2, 4, 5]
irb(main):002:0> [1,2,3,4,5].sample(3)
=> [2, 5, 3]
and for ruby 1.8 something like this:
def sample(arr, n)
arr.shuffle[0...n]
end
irb(main):009:0> sample([1,2,3,4,5], 3)
=> [5, 1, 3]
irb(main):010:0> sample([1,2,3,4,5], 3)
=> [3, 4, 2]

#users.each do |user| --- Is there a way to do this for multiple objects

I'm currently doing:
#users.each do |user|
Given I have #users, #users1, #users2
Is there a way to do:
[#users, #users1, #users2].each do |user|
Having the each loop go through all the objects?
Thanks
You can concatenate the arrays and iterate the result:
(#users + #users1 + #users2).each do |user|
...
end
It may be tempting to use flatten, but you should be careful with it. A simple no-arguments flatten call doesn't behave the way you expect if any elements of any array are arrays themselves:
users, users1, users2 = [1,2], [ [3,4], [5,6] ], [ 7,8]
puts [users,users1,users2].flatten.inspect
# Will print [1, 2, 3, 4, 5, 6, 7, 8]
# Two small arrays are smashed!!!
However, as jleedev suggests in one of the comments, flatten function in Ruby may accept an integer argument that defines the maximum level the arrays will be smashed to, so:
puts [users,users1,users2].flatten(1).inspect
# Will print [1, 2, [3, 4], [5, 6], 7, 8]
# Just as planned.
[#users,#users1,#users2].flatten.each do |user|
Various techniques:
# Concatenate
(#users+#users1+#users2).each{ ... }
# Splat (Ruby 1.9 only)
[*#users,*#users1,*#users2].each{ ... }
# Interleave them
#users.zip( #users1, #users2 ).each{ |u,u1,u2| ... }

Resources