A more balanced array manipulation than each_slice? - ruby-on-rails

I have an array of 10 items and I want to split it up into 3 sections that look like this:
[1, 2, 3, 4]
[5, 6, 7]
[8, 9, 10]
Using each_slice I can get close:
a = *(1..10)
a.each_slice(4) # use 4 so I can fit everything into 3 sections
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10]
But I want the first format which is more evenly distributed. I can do it writing my own method. But is there a built in way to do this in ruby 1.9+?
Update:
Since there's no built in way I'd like to change my question to - how would you implement it?

Here's my implementation
def chunk(a, pieces)
size = a.size / pieces
extra = a.size % pieces
chunks = []
start = 0
1.upto(pieces) do |i|
last = (i <= extra) ? size.next : size
chunks << a.slice(start, last)
start = chunks.flatten.size
end
chunks
end
call it like so
a = *(1..10)
puts chunk(a, 3)
will output
[1, 2, 3, 4]
[5, 6, 7]
[8, 9, 10]
If piece size is too big it pads with empty arrays
a = *(1..10)
puts chunk(a, 14)
will output
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [], [], [], []]

Related

Rails: random unique array of fixed length

How can I ensure uniqueness in this array while maintaining its length at 5?
def fixed
5.times.collect { SecureRandom.random_number(10) }
end
This behaviour seems odd:
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 3]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3]
When the number of possible values is small – like 10 in your example – then I would generate an array with all options and just pick a random sample of entries:
(0..9).to_a.sample(5)
If the number of possible values is huge then generation all values first is certainly not an option. Then I would generate a random value as long as the array doesn't contain enough entries:
require 'set'
values = Set.new
until values.length == 5 do
values.add(SecureRandom.random_number(1_000_000))
end
values.to_a
Note the I am using a Set to ensure the uniqueness of the values in the second version.
Using SecureRandom
def fixed
unique_numbers = []
5.times.collect do
loop do
number = SecureRandom.random_number(10)
break number unless unique_numbers.include?(number)
end
end
end
And if you want to generate unique numbers between 1 to 10, then you can create array of 1 to 10 and use shuffle or sample to get random numbers.
Using shuffle
> (0...10).to_a.shuffle.take(5)
=> [4, 0, 1, 3, 7]
> (0...10).to_a.shuffle.take(5)
=> [6, 2, 3, 9, 1]
> (0...10).to_a.shuffle.take(5)
=> [9, 2, 5, 8, 4]
> (0...10).to_a.shuffle.take(5)
=> [5, 0, 6, 8, 7]
> (0...10).to_a.shuffle.take(5)
=> [2, 7, 1, 5, 0]
Using sample
> (1..10).to_a.sample(5)
=> [4, 6, 3, 2, 7]
> (1..10).to_a.sample(5)
=> [5, 8, 2, 3, 7]
> (1..10).to_a.sample(5)
=> [2, 5, 6, 1, 3]
> (1..10).to_a.sample(5)
=> [8, 5, 10, 9, 3]
> (1..10).to_a.sample(5)
=> [8, 1, 5, 3, 4]
You can also pass SecureRandom custom random generator as an argument with sample
> (1..10).to_a.sample(5, random: SecureRandom)
=> [6, 3, 4, 7, 10]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [7, 4, 8, 1, 5]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [8, 3, 9, 5, 10]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [6, 8, 9, 2, 1]
> (1..10).to_a.sample(5, random: SecureRandom)
=> [9, 10, 1, 8, 2]
Just out of curiosity, using Enumerable#cycle infinite generator.
MAX = 10
SIZE = 5
[MAX].cycle.inject(Set.new) do |acc, max|
break acc if acc.size >= SIZE
acc << SecureRandom.random_number(max)
end
#⇒ #<Set: {2, 1, 7, 0, 9}>
or even with generic loop:
loop.each_with_object(Set.new) do |_, acc|
break acc if acc.size >= SIZE
acc << SecureRandom.random_number(10)
end
#⇒ #<Set: {2, 6, 7, 1, 3}>
One way would be to generate a range of numbers from 0 to 10 and
then shuffle them to get the unique random numbers.
You can convert that range to Array using to_a and shuffle them using shuffle
You can do something like this:
(0..10).to_a.shuffle[0..4] # => [8, 6, 1, 9, 10]
[0..4] will give you the first 5 shuffled elements.

Is there an alternative to use of the method Array#product? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am using the method Array#product with several arguments that are large arrays. The receiver is also a large array. Consequently, the resulting array is huge and consumes an excessive amount of memory. I need all the combinations produced but I don't need them in a single array.
I would like to know if there is an alternative to using that method that requires less memory. Computational time is not really an issue.
You can use the block form of product (see https://apidock.com/ruby/Array/product).
For example the following makes my ruby process unresponsive and I have to kill -9 it:
list = Array.new(100_000)
list.product list
However with the block form I can control+c to stop it any time:
list.product(list) do |combination|
puts combination.join(",")
end
Let me begin with the problem of computing the product of two arrays. Later I will address the more general problem.
arr1 = [1,2,3]
arr2 = [4,5,6]
arr1.product(arr2)
#=> [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5],
# [2, 6], [3, 4], [3, 5], [3, 6]]
To reduce memory requirements--that is, avoiding the construction of the above array--you can construct an enumerator.
product_enum = Enumerator.new do |y|
arr1.size.times do |i|
e1 = arr1[i]
arr2.size.times { |j| y << [e1, arr2[j]] }
end
end
#=> #<Enumerator: 3<Enumerator::Generator:0x00...20>:each>
loop do
p product_enum.next
end
[1, 4]
[1, 5]
[1, 6]
[2, 4]
[2, 5]
[2, 6]
[3, 4]
[3, 5]
[3, 6]
To reuse the enumerator execute product_enum.rewind. See Enumerator::new and Enumerator#rewind. Enumerator::new's block variable y is called a yielder. An enumerator can be thought of as a machine that generates values following certain rules.
Ruby's methods Array#combination, Array#permutation and others return enumerators, but for reasons that are unknown to me, Array#product returns an array. (Perhaps a reader can explain why in a comment.)
We can construct an Array method product_enum that returns an enumerator, which you could use for your problem.
class Array
def product_enum(*arr)
Enumerator.new do |y|
self.size.times do |i|
e1 = self[i]
product_enum_recurse(y,[e1],*arr)
end
end
end
def product_enum_recurse(y,a,*arr)
v, *rest = arr
v.size.times do |i|
if rest.empty?
y << [*a, v[i]]
else
product_enum_recurse(y,[*a, v[i]],*rest)
end
end
end
end
e = [1,2].product_enum([3,4], [5,6], [7,8])
#=> #<Enumerator: #<Enumerator::Generator:0x00...0>:each>
loop do
p e.next
end
[1, 3, 5, 7]
[1, 3, 5, 8]
[1, 3, 6, 7]
[1, 3, 6, 8]
[1, 4, 5, 7]
[1, 4, 5, 8]
[1, 4, 6, 7]
[1, 4, 6, 8]
[2, 3, 5, 7]
[2, 3, 5, 8]
[2, 3, 6, 7]
[2, 3, 6, 8]
[2, 4, 5, 7]
[2, 4, 5, 8]
[2, 4, 6, 7]
[2, 4, 6, 8]

Is it possible to get the difference from two dynamic arrays regardless of their order?

If I have two arrays and I try to find their difference..
[1, 2, 3, 2, 6, 7] - [2, 1]
I get :
[3, 6, 7]
But if I flip those arrays around
[2, 1] - [1, 2, 3, 2, 6, 7]
I get :
[]
My question is, being that my two arrays are dynamic, I need to know if there is a difference in between both arrays regardless of their order. What's the simplest expression to find that?
You can define it:
class Array
def diff(o)
(o - self) + (self - o) # alternatively: (o + self) - (o & self)
end
end
[2, 1].diff [1, 2, 3, 2, 6, 7] # [3, 6, 7]
[1, 2, 3, 2, 6, 7].diff [2, 1] # [3, 6, 7]
[2, 3, 3, 1].diff [2, 4, 5] # [4, 5, 3, 3, 1]
[2, 4, 5].diff [2, 3, 3, 1] # [3, 3, 1, 4, 5]
The correct answer probably depends on what you want in the end, though, as the second two examples above show.
If you only want unique values, you'll want to convert the two inputs to sets first, and return the result as an array:
class Array
def diff(o)
(o.to_set ^ to_set).to_a # or simply (o.to_set ^ self).to_a
end
end
[2, 4, 5].diff [2, 3, 3, 1] # [4, 5, 3, 1]
(There might be a built-in Rails method, too.)
Using Set#^:
require 'set'
([2, 1].to_set ^ [1, 2, 3, 2, 6, 7]).to_a
# => [3, 6, 7]
([1, 2, 3, 2, 6, 7].to_set ^ [2, 1]).to_a
# => [3, 6, 7]
According to the documentation:
Set#^ returns a new set containing elements exclusive between the set and
the given enumerable object.

Dividing elements of a ruby array into an exact number of (nearly) equal-sized sub-arrays [duplicate]

This question already has answers here:
How to chunk an array in Ruby
(2 answers)
Closed 4 years ago.
I need a way to split an array in to an exact number of smaller arrays of roughly-equal size. Anyone have any method of doing this?
For instance
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
groups = a.method_i_need(3)
groups.inspect
=> [[1,2,3,4,5], [6,7,8,9], [10,11,12,13]]
Note that this is an entirely separate problem from dividing an array into chunks, because a.each_slice(3).to_a would produce 5 groups (not 3, like we desire) and the final group may be a completely different size than the others:
[[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13]] # this is NOT desired here.
In this problem, the desired number of chunks is specified in advance, and the sizes of each chunk will differ by 1 at most.
You're looking for Enumerable#each_slice
a = [0, 1, 2, 3, 4, 5, 6, 7]
a.each_slice(3) # => #<Enumerator: [0, 1, 2, 3, 4, 5, 6, 7]:each_slice(3)>
a.each_slice(3).to_a # => [[0, 1, 2], [3, 4, 5], [6, 7]]
Perhaps I'm misreading the question since the other answer is already accepted, but it sounded like you wanted to split the array in to 3 equal groups, regardless of the size of each group, rather than split it into N groups of 3 as the previous answers do. If that's what you're looking for, Rails (ActiveSupport) also has a method called in_groups:
a = [0,1,2,3,4,5,6]
a.in_groups(2) # => [[0,1,2,3],[4,5,6,nil]]
a.in_groups(3, false) # => [[0,1,2],[3,4], [5,6]]
I don't think there is a ruby equivalent, however, you can get roughly the same results by adding this simple method:
class Array; def in_groups(num_groups)
return [] if num_groups == 0
slice_size = (self.size/Float(num_groups)).ceil
groups = self.each_slice(slice_size).to_a
end; end
a.in_groups(3) # => [[0,1,2], [3,4,5], [6]]
The only difference (as you can see) is that this won't spread the "empty space" across all the groups; every group but the last is equal in size, and the last group always holds the remainder plus all the "empty space".
Update:
As #rimsky astutely pointed out, the above method will not always result in the correct number of groups (sometimes it will create multiple "empty groups" at the end, and leave them out). Here's an updated version, pared down from ActiveSupport's definition which spreads the extras out to fill the requested number of groups.
def in_groups(number)
group_size = size / number
leftovers = size % number
groups = []
start = 0
number.times do |index|
length = group_size + (leftovers > 0 && leftovers > index ? 1 : 0)
groups << slice(start, length)
start += length
end
groups
end
Try
a.in_groups_of(3,false)
It will do your job
As mltsy wrote, in_groups(n, false) should do the job.
I just wanted to add a small trick to get the right balance
my_array.in_group(my_array.size.quo(max_size).ceil, false).
Here is an example to illustrate that trick:
a = (0..8).to_a
a.in_groups(4, false) => [[0, 1, 2], [3, 4], [5, 6], [7, 8]]
a.in_groups(a.size.quo(4).ceil, false) => [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
This needs some better cleverness to smear out the extra pieces, but it's a reasonable start.
def i_need(bits, r)
c = r.count
(1..bits - 1).map { |i| r.shift((c + i) * 1.0 / bits ) } + [r]
end
> i_need(2, [1, 3, 5, 7, 2, 4, 6, 8])
=> [[1, 3, 5, 7], [2, 4, 6, 8]]
> i_need(3, [1, 3, 5, 7, 2, 4, 6, 8])
=> [[1, 3, 5], [7, 2, 4], [6, 8]]
> i_need(5, [1, 3, 5, 7, 2, 4, 6, 8])
=> [[1, 3], [5, 7], [2, 4], [6], [8]]

Calculate differences between array elements

Given a sorted array of n integers, like the following:
ary = [3, 5, 6, 9, 14]
I need to calculate the difference between each element and the next element in the array. Using the example above, I would end up with:
[2, 1, 3, 5]
The beginning array may have 0, 1 or many elements in it, and the numbers I'll be handling will be much larger (I'll be using epoch timestamps). I've tried the following:
times = #messages.map{|m| m.created_at.to_i}
left = times[1..times.length-1]
right = times[0..times.length-2]
differences = left.zip(right).map { |x| x[0]-x[1]}
But my solution above is both not optimal, and not ideal. Can anyone give me a hand?
>> ary = [3, 5, 6, 9, 14] #=> [3, 5, 6, 9, 14]
>> ary.each_cons(2).map { |a,b| b-a } #=> [2, 1, 3, 5]
Edit:
Replaced inject with map.
Similar but more concise:
[3, 5, 6, 9, 14].each_cons(2).collect { |a,b| b-a }
An alternative:
a.map.with_index{ |v,i| (a[i+1] || 0) - v }[0..-2]
Does not work in Ruby 1.8 where map requires a block instead of returning an Enumerator.

Resources