如何对数组中的项进行排序以匹配它在另一个数组中出现的顺序

时间:2013-01-28 19:25:52

标签: ruby-on-rails ruby arrays refactoring

我想从数据库中的电影中获取评级并返回一系列独特评级,按以下顺序排序:

G PG PG-13 R NC-17

普通Array#sort还不够:

["PG-13", "PG", "NC-17", "G", "R"].sort
# => ["G", "NC-17", "PG", "PG-13", "R"]

以下代码为我提供了我想要的内容,但似乎有更好的方法来编写它而不必使用delete<<。任何想法都将不胜感激。

class Movie < ActiveRecord::Base
  def self.all_ratings
    allRatings = []
    Movie.all.each do |movie|
      unless allRatings.include?(movie.rating)
        allRatings << movie.rating
      end
    end
    if allRatings.include?("NC-17")
      allRatings.sort!
      allRatings.delete("NC-17")
      allRatings << "NC-17"
      return allRatings
    else
      return allRatings.sort
    end
  end
end

更新: 使用Sergio的提示,我能够重构代码。如果有人有其他想法,我会很感激反馈。

class Movie < ActiveRecord::Base

  def self.all_ratings
    allRatings = []
    Movie.all.each do |movie|
      unless allRatings.include?(movie.rating)
        allRatings << movie.rating
      end
    end
    allRatings.sort_by! {|t| t == 'NC-17' ? 'ZZZ' : t}
  end
end

更新: 使用ByScripts提示,此代码运行良好且非常简洁。我不得不从Rails 3.1.0升级到Rails 3.2.8以获得pluck方法。看起来它是在3.2.1中引入的。

我还必须添加.sort才能获得所需的输出。

class Movie < ActiveRecord::Base

  def self.all_ratings
    all_ratings = Movie.pluck(:rating).uniq.sort# don't retrieve unnecessary datas
    all_ratings << all_ratings.delete('NC-17') # directly inject NC-17 at the end if exists
    all_ratings.compact # remove nil values
  end

end

4 个答案:

答案 0 :(得分:2)

你可以使用这个小技巧

sorted = ["PG-13", "PG", "NC-17", "G", "R"].sort_by {|t| t == 'NC-17' ? 'ZZZ' : t }

sorted # => ["G", "PG", "PG-13", "R", "NC-17"]

基本上,出于分类目的,您可以将“NC-17”替换为最后排序的“ZZZ”。

答案 1 :(得分:2)

这应该有效:

def self.all_ratings
  all_ratings = Movies.order(:rating).pluck(:rating).uniq # don't retrieve unnecessary datas
  all_ratings << all_rating.delete('NC-17') # directly inject NC-17 at the end if exists
  all_ratings.compact # remove nil values
end

您也可以Movies.uniq.pluck(:rating)

执行SELECT DISTINCT查询(其中pluck.uniq过滤数组)。不知道是否存在性能影响(可能是内存占用较少?)。

无论如何,两者都应该是一样的。

答案 2 :(得分:1)

我喜欢塞尔吉奥的伎俩,但是如果您正在寻找原始代码的更简单版本,但仍有delete<<,请尝试使用

def sort(ratings)
  ratings.sort!
  return ratings unless ratings.include?("NC-17")

  ratings.delete("NC-17")

  ratings << "NC-17"
end

答案 3 :(得分:1)

class Movie < ActiveRecord::Base
  RatingOrder = %w[G PG PG-13 R NC-17]
  def self.all_ratings
    RatingOrder & Movie.all.map(&:rating)
  end
end