1

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 queryeven 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.

5
  • ['it', 'blue', 'green'] is not a match, because no students have green. Commented Sep 8, 2016 at 7:58
  • Here's code: pastebin.com/5X4WwmJL. The core of it is the .grep(Regexp.new(keyword)) part. It filters array, leaving only those elements which contain the keyword. Commented Sep 8, 2016 at 8:00
  • if any of the words or partial words match it is a match, so ['it', 'blue', 'green'] is a match Commented Sep 8, 2016 at 8:05
  • Well, replace .all? with .any? then. Commented Sep 8, 2016 at 8:13
  • Yeah!!! This works like a charm with .any? and downcasing my search and keyword arrays! Can you post the code as an answer so I can mark it. Thank you! Commented Sep 8, 2016 at 17:34

3 Answers 3

1

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
Sign up to request clarification or add additional context in comments.

Comments

1

So basically as long as at least one element matches, it's a match?

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

This is fairly preformant because not all search terms and keywords are examined once it's determined that the student is a match.

Comments

0

Not the most efficient, but straight-forward and does what you want:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.