Since you know the desired order there's no need to sort the array. Here's one way you could do that. (I've called your array of hashes bible.)
bible.group_by { |h| h[:book] }.values_at(*array).flatten
#=> [{:book=>"Matthew", :chapter=>"4", :section=>"new_testament"},
# {:book=>"Matthew", :chapter=>"22", :section=>"new_testament"},
# {:book=>"Mark", :chapter=>"6", :section=>"new_testament"},
# {:book=>"Acts", :chapter=>"9", :section=>"new_testament"},
# {:book=>"Acts", :chapter=>"17", :section=>"new_testament"},
# {:book=>"1John", :chapter=>"1", :section=>"new_testament"},
# {:book=>"1John", :chapter=>"1", :section=>"new_testament"}]
Since Enumerable#group_by, Hash#values_at and Array#flatten each require just one pass through the array bible this may be faster than sorting when bible is large.
Here are the steps.
h = bible.group_by { |h| h[:book] }
#=> {"Matthew"=>[{:book=>"Matthew", :chapter=>"4", :section=>"new_testament"},
# {:book=>"Matthew", :chapter=>"22", :section=>"new_testament"}],
# "Mark" =>[{:book=>"Mark", :chapter=>"6", :section=>"new_testament"}],
# "1John" =>[{:book=>"1John", :chapter=>"1", :section=>"new_testament"},
# {:book=>"1John", :chapter=>"1", :section=>"new_testament"}],
# "Acts" =>[{:book=>"Acts", :chapter=>"9", :section=>"new_testament"},
# {:book=>"Acts", :chapter=>"17", :section=>"new_testament"}]
# }
a = h.values_at(*array)
#=> h.values_at('Matthew', 'Mark', 'Acts', '1John')
#=> [[{:book=>"Matthew", :chapter=>"4", :section=>"new_testament"},
# {:book=>"Matthew", :chapter=>"22", :section=>"new_testament"}],
# [{:book=>"Mark", :chapter=>"6", :section=>"new_testament"}],
# [{:book=>"Acts", :chapter=>"9", :section=>"new_testament"},
# {:book=>"Acts", :chapter=>"17", :section=>"new_testament"}],
# [{:book=>"1John", :chapter=>"1", :section=>"new_testament"},
# {:book=>"1John", :chapter=>"1", :section=>"new_testament"}]]
Lastly, a.flatten returns the array shown earlier.
Let's do a benchmark.
require 'fruity'
@bible = [
{:book=>"Matthew",
:chapter=>"4",
:section=>"new_testament"},
{:book=>"Matthew",
:chapter=>"22",
:section=>"new_testament"},
{:book=>"Mark",
:chapter=>"6",
:section=>"new_testament"},
{:book=>"1John",
:chapter=>"1",
:section=>"new_testament"},
{:book=>"1John",
:chapter=>"1",
:section=>"new_testament"},
{:book=>"Acts",
:chapter=>"9",
:section=>"new_testament"},
{:book=>"Acts",
:chapter=>"17",
:section=>"new_testament"}]
@order = ['Matthew', 'Mark', 'Acts', '1John']
def bench_em(n)
arr = (@bible*((n/@bible.size.to_f).ceil))[0,n].shuffle
puts "arr contains #{n} elements"
compare do
_sort { arr.sort { |h1,h2| @order.index(h1[:book]) <=>
@order.index(h2[:book]) }.size }
_sort_by { arr.sort_by { |h| @order.find_index(h[:book]) }.size }
_sort_by_with_hash {[email protected]_index.to_h;
arr.sort_by {|b| ord[b[:book]]}.size}
_values_at { arr.group_by { |h| h[:book] }.values_at(*@order).flatten.size }
end
end
@maxpleaner, @ChaitanyaKale and @Michael Kohl contributed _sort, _sort_by, and sort_by_with_hash, respectively.
bench_em 100
arr contains 100 elements
Running each test 128 times. Test will take about 1 second.
_sort_by is similar to _sort_by_with_hash
_sort_by_with_hash is similar to _values_at
_values_at is faster than _sort by 2x ± 1.0
bench_em 1_000
arr contains 1000 elements
Running each test 16 times. Test will take about 1 second.
_sort_by_with_hash is similar to _values_at
_values_at is similar to _sort_by
_sort_by is faster than _sort by 2x ± 0.1
bench_em 10_000
arr contains 10000 elements
Running each test once. Test will take about 1 second.
_values_at is faster than _sort_by_with_hash by 10.000000000000009% ± 10.0%
_sort_by_with_hash is faster than _sort_by by 10.000000000000009% ± 10.0%
_sort_by is faster than _sort by 2x ± 0.1
bench_em 100_000
arr contains 100000 elements
Running each test once. Test will take about 3 seconds.
_values_at is similar to _sort_by_with_hash
_sort_by_with_hash is similar to _sort_by
_sort_by is faster than _sort by 2x ± 0.1
Here's a second run.
bench_em 100
arr contains 100 elements
Running each test 128 times. Test will take about 1 second.
_sort_by_with_hash is similar to _values_at
_values_at is similar to _sort_by
_sort_by is faster than _sort by 2x ± 0.1
bench_em 1_000
arr contains 1000 elements
Running each test 8 times. Test will take about 1 second.
_values_at is faster than _sort_by_with_hash by 10.000000000000009% ± 10.0%
_sort_by_with_hash is similar to _sort_by
_sort_by is faster than _sort by 2.2x ± 0.1
bench_em 10_000
arr contains 10000 elements
Running each test once. Test will take about 1 second.
_values_at is similar to _sort_by_with_hash
_sort_by_with_hash is similar to _sort_by
_sort_by is faster than _sort by 2x ± 1.0
bench_em 100_000
arr contains 100000 elements
Running each test once. Test will take about 3 seconds.
_sort_by_with_hash is similar to _values_at
_values_at is similar to _sort_by
_sort_by is faster than _sort by 2x ± 0.1