Leo Net Leo Net - 3 months ago 7
Ruby Question

Select Array Items Containing Elements From Other Array, Allow Partial Match

Let's say I have a program like this:

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


I'm trying to accomplish the following:

1) select an array of Students that match my
query
with my Student.keywords array

2) my
query
is also an array of single words

3) if
query
even partially matches
keyword
it's a "match"

For example keywords for
student1
are:
['john', 'smith', 'math', 'blue']


any of the following
query
arrays IS A MATCH

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


any of the following
query
arrays IS NOT A MATCH

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


How would I write this? I've been scratching my head for hours and no joy!

Also, I do need this to be fairly performant since I may need to iterate over 1000 or more array elements. I need a pure ruby solution as well.

Answer

Here's working code. Plain ruby and all. The core of it is the .grep(Regexp.new(keyword)) part. It filters array, leaving only those elements which contain the 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