Ruby:实例数组多关键词搜索的最优实现方案
重构电视节目表多关键词搜索的简洁高效方案
嘿,我懂你现在的痛点——维护10个重复的select_by_*类方法不仅繁琐,还容易因为复制粘贴出问题。咱们把这些重复逻辑抽象出来,用更简洁、可维护的方式实现相同的功能!
核心思路:抽象通用搜索逻辑
原来的每个select_by_*方法本质都是「针对某个字段,检查是否包含任意给定关键词」,所以我们可以把这个逻辑抽成一个通用方法,再动态生成原来的方法名(如果需要保留原有调用方式的话)。
第一步:实现通用搜索方法
先写一个基础的通用类方法,支持指定字段和关键词数组:
class TVProgram def self.select_by_field(field_name, keywords) # 遍历所有节目实例,筛选符合条件的 all.select do |program| # 动态获取实例的字段值(假设你的节目实例有对应字段的getter方法) field_value = program.send(field_name) # 先判断字段值不为空,再检查是否包含任意关键词 field_value.present? && keywords.any? { |keyword| field_value.include?(keyword) } end end end
这里的send(field_name)是关键——它能动态调用实例的对应字段方法(比如:title就会调用program.title),避免重复写相同的遍历逻辑。
第二步:批量生成原有select_by_*方法
如果想保留原来的方法名(比如select_by_title、select_by_director),不用一个个手写,用Ruby的元编程批量生成:
class TVProgram # 把所有需要支持搜索的字段列在这里,以后加新字段只要加个符号就行 SEARCHABLE_FIELDS = [:title, :director, :actor, :genre, :description, :channel, :air_date, :episode_name, :production_company, :rating] # 批量生成select_by_xxx方法 SEARCHABLE_FIELDS.each do |field| define_singleton_method("select_by_#{field}") do |keywords| select_by_field(field, keywords) end end # 通用搜索方法 def self.select_by_field(field_name, keywords) all.select do |program| field_value = program.send(field_name) field_value.present? && keywords.any? { |keyword| field_value.include?(keyword) } end end end
现在你原来的调用方式完全不受影响——TVProgram.select_by_title(["科幻", "悬疑"])和之前一模一样,但背后只复用了一套逻辑。
优化建议:让搜索更高效
如果你的节目数据量很大,还可以做这些优化:
- 大小写不敏感搜索:如果不需要区分大小写,把字段值和关键词统一转成小写/大写,避免漏匹配:
field_value.downcase.include?(keyword.downcase) - 提前预处理字段:在节目实例初始化时,就把需要搜索的字段转成小写并缓存,避免每次搜索都重复转换:
然后搜索时直接用class TVProgram attr_reader :title, :lowercase_title def initialize(title) @title = title @lowercase_title = title.downcase end endlowercase_title,减少计算量。 - 索引优化(大数据量场景):如果数组里有上万条节目,可以提前为每个搜索字段建立哈希索引,比如:
这样搜索时不用遍历整个数组,直接查索引,速度会快很多。class TVProgram @@title_index = nil def self.build_title_index @@title_index = Hash.new { |h, k| h[k] = [] } all.each do |program| # 这里可以按关键词拆分,或者直接存原字段值,根据需求调整 @@title_index[program.lowercase_title] << program end end def self.select_by_title(keywords) build_title_index unless @@title_index keywords.flat_map { |kw| @@title_index.keys.grep(/#{kw.downcase}/) }.uniq.flat_map { |k| @@title_index[k] } end
调用示例
不管是用原来的方法名还是通用方法,都能正常工作:
# 用原有方法名 action_shows = TVProgram.select_by_genre(["Action", "Adventure"]) # 用通用方法 netflix_shows = TVProgram.select_by_field(:channel, ["Netflix"])
内容的提问来源于stack exchange,提问作者TylerP3358
相关产品推荐
相关产品推荐

