Reputation: 43
I am trying to get a custom sort working. Eventually I will try to apply this to a database query but before that I want to prove the logic works in an in-memory sort. The problem is, I cannot get even the most simplest of sort methods to work!
Sort method:
def self.sort!(reputation_alerts)
#reputation_alerts.sort { |a, b| b <=> a }
reputation_alerts.sort { |a, b| b.id <=> a.id }
end
I had hoped the first sort (currently commented out) would return reputation_alerts in reverse order. It did nothing.
I had hoped the second sort would return reputation alerts sorted by id in reverse order. It did nothing.
I am testing the sort by iterating through the returned reputation_alerts object and dumping the contents of each one; every time the "sorted" results are the same as the original order...
Test method:
test "ReputationAlertSort PassedArray SortsArrayCorrectly" do
@customer = customers(:one)
@customer.update(keywords: { 1 => "bob", 2 => "wibble", 3 => "random" } )
@customer.get_keywords
@customer.reputation_alerts.clear
@customer.reputation_alerts.build(
id: 2, rep_keyword: "random", search_engine: "bing", rank: 1)
@customer.reputation_alerts.build(
id: 1, rep_keyword: "wibble", search_engine: "google", rank: 2)
@customer.reputation_alerts.build(
id: 3, rep_keyword: "wibble", search_engine: "google", rank: 1)
@customer.reputation_alerts.build(
id: 4, rep_keyword: "wibble", search_engine: "yahoo", rank: 2)
@customer.reputation_alerts.build(
id: 5, rep_keyword: "wibble", search_engine: "yahoo", rank: 1)
@customer.reputation_alerts.build(
id: 6, rep_keyword: "bob", search_engine: "google", rank: 2)
@customer.reputation_alerts.build(
id: 7, rep_keyword: "bob", search_engine: "yahoo", rank: 1)
@customer.reputation_alerts.build(
id: 8, rep_keyword: "random", search_engine: "bing", rank: 2)
ReputationAlert.sort!(@customer.reputation_alerts)
assert_sorted(1, 7)
assert_sorted(2, 6)
assert_sorted(3, 5)
assert_sorted(4, 4)
assert_sorted(5, 3)
assert_sorted(6, 2)
assert_sorted(7, 1)
assert_sorted(8, 8)
end
def assert_sorted(post_sort_position, pre_sort_position)
assert_equal post_sort_position,
@customer.reputation_alerts[pre_sort_position - 1].id, dump
end
def dump
dump = ""
@customer.reputation_alerts.each do |a|
dump += "\n#{a.id}, #{a.rep_keyword}, #{a.search_engine}, #{a.rank}"
end
dump
end
Any suggestions? Once I can get this working I can move on to the actual (complex) sort...
EDIT:
I thought the type of object I was trying to sort might be posing a problem, so I tried an even more basic sort:
temp.sort
:
test "ReputationAlertSort PassedArray SortsArrayCorrectly" do
temp = 1..7
temp.sort { |a, b| b <=> a }
raise "#{temp}"
end
This returns temp still unsorted. (Kind of expected due to the lack of a !)
temp.sort!
test "ReputationAlertSort PassedArray SortsArrayCorrectly" do
temp = 1..7
temp.sort! { |a, b| b <=> a }
raise "#{temp}"
end
This gives the error: NoMethodError: undefined method `sort!' for 1..7:Range
temp = temp.sort!
test "ReputationAlertSort PassedArray SortsArrayCorrectly" do
temp = 1..7
temp = temp.sort { |a, b| b <=> a }
raise "#{temp}"
end
This DOES work, at least, so maybe I can use that as a starting point to figure this out...
Upvotes: 1
Views: 138
Reputation: 43
The other answers have really helped resolve this, but there is a little more to it than that so here is a complete run-through of the solution:
1: .sort!
will not work for the collection I am using. It is a collection of child elements (specifically using a has_many association). Attempting to call .sort!
gives the error:
NoMethodError: undefined method `sort!' for #<ReputationAlert::ActiveRecord_Associations_CollectionProxy:0x61729b0>
2: Owner.Collection = Owner.Collection.sort { <snip> }
will not work. Assigning the result of sorting a collection to the original collection itself results in an unsorted collection in this instance. Once again, I suspect this may be behaviour specific to ActiveRecord associations.
3: new_variable = Owner.Collection.sort { <snip> }
works. Taking the result of the sort and assigning it to some other arbitrary variable and using that gives the desired (sorted) result.
In conclusion it seems the problem here is not the sort functionality itself, but specific issues with sort functionality on ActiveRecord associations.
(Of course, this may actually be a redundant concern as the plan is to eventually have the ActiveRecord query handle the sorting for us...)
Upvotes: 0
Reputation: 50057
Your method sort!
returns a sorted array, but you don't do anything with the result? The naming seems to indicate you do inplace sorting, but your method does not. So either use .sort!
inside the method too, or rename the method to just sort
and store the result and check that.
Secondly, your edit: temp
is a range, to make sure sort works, you have to convert it to an array first. So if you write
temp = (1..7).to_a
it should work.
Upvotes: 1
Reputation: 1318
You're not overriding the reputation_alerts object with your sort. Just add an exclamation mark to the sort itself:
def self.sort!(reputation_alerts)
#reputation_alerts.sort! { |a, b| b <=> a }
reputation_alerts.sort! { |a, b| b.id <=> a.id }
end
Edit:
Oops, I didn't think about it calling itself. I did try adding a similar method to my model and it seems to be working OK.
def self.sorting(models)
models.sort!{ |a, b| b.id <=> a.id }
end
Model.sorting(models).map(&:id)
=> [17,16,15...]
On another note, all of this seems redundant, as .sort method should exist for any collection as it's in Enumerable. You should just be able to call reputation_alerts.sort! { |a, b| b.id <=> a.id }
.
Edit2:
If you don't have sort! for some reason, just use the .sort as in your added question info:
models = models.sort{...}
This is the same as sort!.
Upvotes: 1