选择包含其他数组元素的数组项,允许部分匹配

时间:2016-09-08 07:48:13

标签: ruby

假设我有一个这样的程序:

class Student
  attr_reader :first_name, :last_name, :subject, :color

  def initialize(first_name, last_name, subject, color)
    @first_name = first_name
    @last_name = last_name
    @subject = subject
    @color = color
  end

  def keywords
    [] << @first_name << @last_name << @subject << @color
  end
end

student1 = Student.new('john', 'smith', 'math', 'blue')
student2 = Student.new('ann', 'smitten', 'english', 'blue')
students = [student1, student2]

students.select do |student|
   ...
end

我正在努力完成以下任务:

1)选择与我的query匹配的学生数组与我的Student.keywords数组

2)我的query也是一个单词数组

3)如果query甚至部分匹配keyword,那么它就是“匹配”

例如student1的关键字是: ['john', 'smith', 'math', 'blue']

以下任何query数组是一个匹配

['j', 'mat']['it', 'blue', 'green']

以下任何query数组都不是匹配

['johny']['johny', 'smithy', 'mathy', 'bluegreen']

我怎么写这个?我一直在挠头几个小时,没有快乐!

另外,我确实需要这个是相当高效的,因为我可能需要迭代超过1000个或更多的数组元素。我也需要纯红宝石溶液。

3 个答案:

答案 0 :(得分:1)

所以基本上只要至少有一个元素匹配,它就匹配了吗?

def match(search_array, student_array)
  # initialize output array
  matching_students = [] 
  # iterate through all students
  student_array.each do |student|
    # this student not yet matching
    matched_student = false
    # iterate through student's keywords
    student.keywords.each do |keyword|
      # iterate through search words
      search_array.each do |word|
        # test for match
        if keyword.start_with? word
          # add to array
          matching_students << student
          # flag current student as matched
          matched_student = true
        end
        # don't continue with search array if student already matched
        break if matched_student
      end
      # don't continue with student.keywords if student already matched
      break if matched_student
    end
  end
  matching_students
end

这是相当有说服力的,因为并非所有搜索字词和关键字在确定学生匹配后都会被检查。

答案 1 :(得分:1)

这是工作代码。纯红宝石和所有。它的核心是.grep(Regexp.new(keyword))部分。它过滤数组,只留下那些包含关键字的元素。

class Student
  attr_reader :first_name, :last_name, :subject, :color

  def initialize(first_name, last_name, subject, color)
    @first_name = first_name
    @last_name = last_name
    @subject = subject
    @color = color
  end

  def keywords
    [ first_name, last_name, subject, color ]
  end
end

class Matcher
  attr_reader :student, :search_keywords

  def initialize(student, search_keywords)
    @student = student
    @search_keywords = search_keywords
  end

  def match?
    search_keywords.any? do |kw|
      student.keywords.grep(Regexp.new(kw)).length > 0
    end
  end
end

def count_results(students, query)
  students.select {|s| Matcher.new(s, query).match? }.length
end

student1 = Student.new('john', 'smith', 'math', 'blue')
student2 = Student.new('ann', 'smitten', 'english', 'blue')
students = [student1, student2]

count_results(students, ['j', 'mat']) # => 1
count_results(students, ['it', 'blue', 'green']) # => 2
count_results(students, ['johny']) # => 0
count_results(students, ['johny', 'smithy', 'mathy', 'bluegreen']) # => 0

答案 2 :(得分:0)

不是最有效率,而是直截了当地做你想做的事情:

class Student
  attr_reader :first_name, :last_name, :subject, :color

  def initialize(first_name, last_name, subject, color)
    @first_name = first_name
    @last_name = last_name
    @subject = subject
    @color = color
  end

  def keywords
    [first_name, last_name, subject, color]
  end

  def matches_search?(search_terms)
    search_terms.any? { |search_term| keywords.find { |kw| kw.match(search_term) } }
  end
end

student1 = Student.new('john', 'smith', 'math', 'blue')
student2 = Student.new('ann', 'smitten', 'english', 'blue')
students = [student1, student2]

should_match = [['j', 'mat'], ['it', 'blue', 'green']]
should_not_match = ['johny'], ['johny', 'smithy', 'mathy', 'bluegreen']

should_match.map { |search| students.select { |student| student.matches_search?(search) } }
# returns an array of 2 times student one

should_not_match.map { |search| students.select { |student| student.matches_search?(search) } }
# two empty arrays, i.e. no student matched