Enumerable#in_order_of (#41333)

* Add Enumerable#in_order_of

* Explain behavior further

* Use Enumerable#in_order_of

* Use Ruby 2.7 #filter_map

* Update activesupport/lib/active_support/core_ext/enumerable.rb

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>

* No need for explaining variable

* Add CHANGELOG entry

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
This commit is contained in:
David Heinemeier Hansson 2021-02-05 17:28:50 +01:00 committed by GitHub
parent 9e8824036d
commit 9cb09411e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 4 deletions

View File

@ -520,10 +520,7 @@ module ActiveRecord
result = except(:limit, :offset).where(primary_key => ids).records
if result.size == ids.size
pk_type = @klass.type_for_attribute(primary_key)
records_by_id = result.index_by(&:id)
ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
result.in_order_of(:id, ids.map { |id| @klass.type_for_attribute(primary_key).cast(id) })
else
raise_record_not_found_exception!(ids, result.size, ids.size)
end

View File

@ -1,3 +1,7 @@
* Add `Enumerable#in_order_of` to put an Enumerable in a certain order by a key.
*DHH*
* `ActiveSupport::Inflector.camelize` behaves expected when provided a symbol `:upper` or `:lower` argument. Matches
`String#camelize` behavior.

View File

@ -191,6 +191,18 @@ module Enumerable
def compact_blank
reject(&:blank?)
end
# Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
# objects in the original enumerable.
#
# [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
# => [ Person.find(1), Person.find(5), Person.find(3) ]
#
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
def in_order_of(key, series)
index_by(&key).values_at(*series).compact
end
end
class Hash

View File

@ -284,4 +284,19 @@ class EnumerableTests < ActiveSupport::TestCase
values.compact_blank!
assert_equal({ b: 1, f: true }, values)
end
def test_in_order_of
values = [ Payment.new(5), Payment.new(1), Payment.new(3) ]
assert_equal [ Payment.new(1), Payment.new(5), Payment.new(3) ], values.in_order_of(:price, [ 1, 5, 3 ])
end
def test_in_order_of_ignores_missing_series
values = [ Payment.new(5), Payment.new(1), Payment.new(3) ]
assert_equal [ Payment.new(1), Payment.new(5), Payment.new(3) ], values.in_order_of(:price, [ 1, 2, 4, 5, 3 ])
end
def test_in_order_of_drops_elements_not_named_in_series
values = [ Payment.new(5), Payment.new(1), Payment.new(3) ]
assert_equal [ Payment.new(1), Payment.new(5) ], values.in_order_of(:price, [ 1, 5 ])
end
end