Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enumerable#in_order_of #41333

Merged
merged 7 commits into from Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 1 addition & 4 deletions activerecord/lib/active_record/relation/finder_methods.rb
Expand Up @@ -520,10 +520,7 @@ def find_some_ordered(ids)
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
dhh marked this conversation as resolved.
Show resolved Hide resolved
raise_record_not_found_exception!(ids, result.size, ids.size)
end
Expand Down
4 changes: 4 additions & 0 deletions activesupport/CHANGELOG.md
@@ -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.

Expand Down
12 changes: 12 additions & 0 deletions activesupport/lib/active_support/core_ext/enumerable.rb
Expand Up @@ -191,6 +191,18 @@ def pick(*keys)
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
Expand Down
15 changes: 15 additions & 0 deletions activesupport/test/core_ext/enumerable_test.rb
Expand Up @@ -284,4 +284,19 @@ def test_hash_compact_blank!
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