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

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]]

Related

Given an array A[] and a number x, check for pair in A[] with sum as x

Given an array A[] and a number x, check for pair in A[] with sum as x. can anyone help me out on this one in rails?
The ruby array #combination method can give you all combinations of array members of a given number of elements.
[1, 2, 3, 4, 5, 6].combination(2).to_a
=> [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], ... [5,6]]
Then you just want to select the elements where they add up to a given number.
[1, 2, 3, 4, 5, 6]combination(2).to_a.select{|comb| comb[0] + comb[1] == 7}
=> [[1, 6], [2, 5], [3, 4]]
To make it work for a different number of combined elements (e.g. 3 instead of 2) you can do...
[1, 2, 3, 4, 5, 6]combination(3).to_a.select{|c| (c.inject(0) {|sum,x| sum + x}) == 7}
This will work for 2, 3, 4, or any number up to the full array size.
It works by
finding combinations of 3
using `#inject' to sum all the elements of each combination
comparing that sum to the target number
You can easily achieve it by own function as:
def sum_as_x?(ary,x)
num=a.find{|e| ary.include?(x-e)}
unless num
puts "not exist"
else
p [x-num,num]
end
end
a = [1,2,3,4,5]
sum_to_x?(a,9)
>> [5, 4]
sum_to_x?(a,20)
>> not exist

Remove redundant or duplicate tuples in Ruby array

Imagine the following Ruby array:
[9, 9, 5, 5, 5, 2, 9, 9]
What's the easiest way of removing redundant tuples, producing an output like the following:
[9, 5, 2, 9]
uniq is not correct because it's examining the entire array. The ordering of the input is important and must be kept. Is there a straightforward approach to this?
Thanks!
I'd do using Enumerable#chunk
2.0.0-p0 :001 > a = [9, 9, 5, 5, 5, 2, 9, 9]
=> [9, 9, 5, 5, 5, 2, 9, 9]
2.0.0-p0 :002 > a.chunk { |e| e }.map(&:first)
=> [9, 5, 2, 9]
I would do it like
b = [];
a.each { |n| b << n if b.last != n }
and b is the result
only one array scan is needed
I like Arup's answer best, but in case you want a method that is compatible with versions that don't have chunk you can do
a = [9, 9, 5, 5, 5, 2, 9, 9]
a.inject([a[0]]) { |b,c| b.last == c ? b : b << c }
# => [9, 5, 2, 9]
This is my version:
a.each_with_object([]) { |el, arr| arr << el if arr.last != el }
#=> [9, 5, 2, 9]
For those who land on this question looking to remove "redundant" values, the OP is trying to remove "repeated consecutive" values, not "redundant" or "duplicate" values and used the wrong word. They are different situations.
For clarification, removing redundant or duplicate values would be:
asdf = [9, 9, 5, 5, 5, 2, 9, 9]
asdf.uniq # => [9, 5, 2]
Or:
asdf & asdf # => [9, 5, 2]
Or:
require 'set'
asdf.to_set.to_a # => [9, 5, 2]
And, yes, I know the OP is asking for a different result. This is to show the answer for the question that was asked, NOT what what would meet the desired output. For that see the selected answer.
This is to show how you could use an enumerator directly, with the methods Enumerator#next and Enumerator#peek.
def purge_conseq_dups(arr)
return arr if arr.empty?
enum = arr.to_enum
a = []
loop do
e = enum.next
a << e unless e == enum.peek
end
a << arr.last
end
asdf = [9, 9, 5, 5, 5, 2, 9, 9]
purge_conseq_dups(asdf) #=> [9, 5, 2, 9]
When e is the last element of the enumerator enum, enum.peek raises a StopInteration exception which is rescued by Kernel#loop, which responds by breaking out of the loop. At that point all that remains is to append the last element of arr to a.
We could write a << e rather than a << arr.last, provided we initialize e prior to the loop (e.g., e = nil) so that the variable will be in scope in the last line.

How do I repeat certain items in an array based on matched values in another array?

I've been trying for a couple weeks to figure this out, but I'm totally stumped.
I have an array that represents item_id's: [2, 4, 5, 6, 2, 3].
I have another array that represents how many times each item shows up: [1, 1, 3, 3, 2, 5] .
I want to check that all items have been completed so I want to create an array that has the total number of item_id's in it. I will compare that array against a completed items array that will be created as the user completes each item, so, from the example above, the array I'm trying to create is:
[2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
EDIT:
I'm building a workout app, so a user has a workout which has many exercises. Each exercise has one or more sets associated with it. The user completes an exercise when he has completed every set for that exercise, and completes a workout when he completes all exercises for that workout. In this question I'm trying to determine when a user has finished a workout.
EDIT 2:
I wish I could award multiple right answers! Thanks everyone!
Ok, #sameera207 suggested one way, then I will suggest another way (functional style):
arr1 = [2, 4, 5, 6, 2, 3]
arr2 = [1, 1, 3, 3, 2, 5]
arr1.zip(arr2).flat_map { |n1, n2| [n1] * n2 }
item_ids = [2, 4, 5, 6, 2, 3]
counts = [1, 1, 3, 3, 2, 5]
item_ids.zip(counts).map{|item_id,count| [item_id]*count}.flatten
=> [2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
What's going on here? Let's look at it step by step.
zip takes two arrays and "zips" them together element-by-element. I did this to create an array of item_id, count pairs.
item_ids.zip(counts)
=> [[2, 1], [4, 1], [5, 3], [6, 3], [2, 2], [3, 5]]
map takes each element of an array and executes a block. In this case, I'm using the * operator to expand each item_id into an array of count elements.
[1]*3 => [1, 1, 1]
[[2, 1], [4, 1], [5, 3], [6, 3], [2, 2], [3, 5]].map{|item_id,count| [item_id]*count}
=> [[2], [4], [5, 5, 5], [6, 6, 6], [2, 2], [3, 3, 3, 3, 3]]
Finally, flatten takes an array of arrays and "flattens" it down into a 1-dimensional array.
[[2], [4], [5, 5, 5], [6, 6, 6], [2, 2], [3, 3, 3, 3, 3]].flatten
=> [2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
ids = [2, 4, 5, 6, 2, 3]
repeats = [1, 1, 3, 3, 2, 5]
result = []
ids.count.times do |j|
repeats[j].times { result << ids[j] }
end
This is a one way of doing it:
a = [2,4,5,6,2,3]
b = [1,1,3,3,2,5]
c = []
a.each.with_index do |index, i|
b[index].to_i.times {c << i }
end
p c

A more balanced array manipulation than each_slice?

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], [], [], [], []]

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