Decision
Thanks to @ sagarpandya82 for the original idea and @Cary Swoveland for finding bugs!
Or use 2 methods:
def safe_transpose_and_flatten(array) l = array.map(&:length).max array.map{|e| e.values_at(0...l)}.transpose.flatten.compact end def sort_by_batches(array) safe_transpose_and_flatten(array.sort.group_by{|i| i}.values) end
Or this one line (split into several lines for relative readability):
def sort_by_batches(array) array.group_by{|i| i }.values
Example
a = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4] sort_by_batches(a)
Actions
Below are the steps for the second array:
[1, 1, 3, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 1, 1] # input {1=>[1, 1, 1, 1], 3=>[3, 3, 3, 3], 2=>[2, 2, 2], 4=>[4, 4, 4], 5=>[5]} # group_by [[1, 1, 1, 1], [3, 3, 3, 3], [2, 2, 2], [4, 4, 4], [5]] # values [[1, 1, 1, 1], [3, 3, 3, 3], [2, 2, 2], [4, 4, 4], [5]] # sort_by -length [[[[[1, 3], 2], 4], 5], [[[[1, 3], 2], 4], nil], [[[[1, 3], 2], 4], nil], [[[[1, 3], nil], nil], nil]] # zip [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 3]] # map(&:flatten) and compact [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 3, 4, 1, 3] # flatten
.reduce(&:zip).map(&:flatten).compact
initially used as a supposedly safe transposition, but it did not work when the first array was smaller than the rest.
The first method uses this answer for transposition, single-line sorts arrays, reducing the length before using zip
.
Job Class Application
Here is an example of the base class Job:
class Job attr_reader :id def initialize(id) @id = id end def self.sort_by_batches(jobs) safe_transpose_and_flatten(jobs.sort_by{|j| j.id}.group_by{|j| j.id}.values) end def to_s "Job %d" % id end end jobs = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4].map{|i| Job.new(i)} Job.sort_by_batches(jobs)
It outputs:
Job 1 Job 2 Job 3 Job 4 Job 1 Job 2 Job 3 Job 4 Job 1 Job 2 Job 3 Job 4