I've assumed:
- the first element of each element of
x
is not necessarily unique;
- all elements of
x
whose first element is the same and whose first element is a member of order_array
appear consecutively in the returned (sorted) array in the order in which those elements appear in x
;
- any elements of
x
whose first element is not a member of order_array
appears in the returned (sorted) array after all elements whose first element is in sorted_array
, and all such elements appear in the returned array (at the end) in the order in which they occur in x
; and
- efficiency is paramount.
x = [
["ready", 5], ["shipped", 1], ["pending", 1], ["refunded", 1], ["originated", 3],
["delivered", 23], ["scheduled", 1], ["ready", 8], ["canceled", 51]
]
order_array = [
"ready", "in_progress", "received", "shipped", "scheduled", "pick_up",
"delivered", "canceled", "failed", "refunded", "refund_failed"
]
order_pos = order_array.each_with_object({}) { |word,h| h[word] = [] }
#=> {"ready"=>[], "in_progress"=>[], "received"=>[], "shipped"=>[],
# "scheduled"=>[], "pick_up"=>[], "delivered"=>[], "canceled"=>[],
# "failed"=>[], "refunded"=>[], "refund_failed"=>[]}
back = x.each_with_index.with_object([]) { |((word,v),i),back|
order_pos.key?(word) ? (order_pos[word] << i) : back << [word,v] }
#=> [["pending", 1], ["originated", 3]]
order_pos.flat_map { |word,offsets| offsets.map { |i| x[i] } }.concat(back)
#=> [["ready", 5], ["ready", 8], ["shipped", 1], ["scheduled", 1],
# ["delivered", 23], ["canceled", 51], ["refunded", 1], ["pending", 1],
# ["originated", 3]]
Note:
order_pos
#=> {"ready"=>[0, 7], "in_progress"=>[], "received"=>[], "shipped"=>[1],
# "scheduled"=>[6], "pick_up"==>[], "delivered"=>[5], "canceled"=>[8],
# "failed"=>[], "refunded"=>[3], "refund_failed"=>[]}
It is necessary to initialise order_pos
in order for its keys to be ordered by order_arr
. This is an example of the worth of a controversial change made in Ruby 1.9 which guaranteed that hash keys will remain in key-insertion order.