Zild
Zild

Reputation: 43

Simple sort doing nothing?

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

Answers (3)

Zild
Zild

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

nathanvda
nathanvda

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

nesiseka
nesiseka

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

Related Questions