Search code examples
ruby-on-railsrubyapiruby-on-rails-5

Multiple field sort_by combination of reverse and not


So I got this array of object from database and I have to sort it in order:

  1. by score (reverse)
  2. by count (reverse)
  3. by name

I've tried all three in reverse but what I need is the last one should not be reverse (DESC) here's my code:


new_data = data.sort_by{ |t| [t.score, t.matches_count, t.name] }.reverse


RESULT

[
            {
                "id": null,
                "team_id": 939,
                "name": "DAV",
                "matches_count": 2,
                "score": 100.0
            },
            {
                "id": null,
                "team_id": 964,
                "name": "SAN",
                "matches_count": 1,
                "score": 100.0
            },
            {
                "id": null,
                "team_id": 955,
                "name": "PAS",
                "matches_count": 1,
                "score": 100.0
            },
            {
                "id": null,
                "team_id": 954,
                "name": "PAR",
                "matches_count": 1,
                "score": 100.0
            },
            {
                "id": null,
                "team_id": 952,
                "name": "NUE",
                "matches_count": 1,
                "score": 100.0
            }
        ]

Expected result should be sorted by name in ASC order not DESC I knew my code is wrong because the t.name is inside .reverse but if I will reorder it by name alone after the first 2 I will get the wrong answer it will just sort all by name not by the 3. I've also tried to .order("name DESC") from query so when reverse it will go ASC but no luck. Thank you!


Solution

  • data = [{:score=>100.0, :matches_count=>2, :name=>"DAV"},
            {:score=>100.0, :matches_count=>1, :name=>"SAN"},
            {:score=>100.0, :matches_count=>1, :name=>"PAS"},
            {:score=>110.0, :matches_count=>1, :name=>"PAR"},
            {:score=>100.0, :matches_count=>1, :name=>"NUE"}] 
    

    data.sort_by{ |h| [-h[:score], -h[:matches_count], h[:name]] }
      #=> [{:score=>110.0, :matches_count=>1, :name=>"PAR"},
      #    {:score=>100.0, :matches_count=>2, :name=>"DAV"},
      #    {:score=>100.0, :matches_count=>1, :name=>"NUE"},
      #    {:score=>100.0, :matches_count=>1, :name=>"PAS"},
      #    {:score=>100.0, :matches_count=>1, :name=>"SAN"}] 
    

    You could also use Array#sort, which does not require that values to be sorted on in descending order be numeric, so long as they are comparable, that is, so long as they respond to the method :<=>.

    data.sort do |t1, t2|
      case t1[:score] <=> t2[:score]
      when -1
         1
      when  1
        -1
      else
        case t1[:matches_count] <=> t2[:matches_count]
        when -1
           1
        when  1
          -1
        else
          t1[:name] <=> t2[:name]
        end
      end
    end
      #=> <as above>