Home > Enterprise >  How to match and replace pattern without regex?
How to match and replace pattern without regex?

Time:06-23

I was recently asked this in an interview and was figuring out a way to do this without using regex in Ruby as I was told it would be a bonus if you can solve it without using regex.

Question: Assume that the hash has 1 million key, value pairs and we have to be able to sub the variables in the string that are between % % this pattern. How would I be able to do this without regex.

We have a string str = "%greet%! Hi there, %var_1% that can be any other %var_2% injected to the %var_3%. Nice!, goodbye)"

we have a hash called dict = { greet: 'Hi there', var_1: 'FIRST VARIABLE', var_2: 'values', var_3: 'string', }

This was my solution:

def template(str, dict)
  vars = value.scan(/%(.*?)%/).flatten
  vars.each do |var|
    value = value.gsub("%#{var}%", dict[var.to_sym])
  end
  value
end

CodePudding user response:

There are many ways to solve this, but you will probably need some kind of parsing and / or lexical analysis if you don't want to use built-in pattern matching.

Let's keep it very simple and say that your string's content falls into two categories: text and variable which are separated by %, e.g.

str = "Hello %name%, hope to see you %when%!"
#      TTTTTT VVVV TTTTTTTTTTTTTTTTTT VVVV T

As you can see, the categories are alternating. We can utilize this and write a little helper method that turns a string into a list of [type, value] pairs, something like this:

def each_part(str)
  return enum_for(__method__, str) unless block_given?

  type = [:text, :var].cycle
  buf = ''

  str.each_char do |char|
    if char != '%'
      buf << char
    else
      yield type.next, buf
      buf = ''
    end
  end
  yield type.next, buf
end

It starts by defining an enumerator that will cycle between the two types and an empty buffer. It will then read each_char from the string. If the char is not %, it will just append it to the buffer and keep reading. Once it encounters a %, it will yield the current buffer along with the type and start a new buffer (next will also switch the type). After the loop ends, it will yield once more to output the remaining characters.

It outputs this kind of data:

each_part(str).to_a
#=> [[:text, "Hello "],
#    [:var, "name"],
#    [:text, ", hope to see you "],
#    [:var, "when"],
#    [:text, "!"]]

We can use this to convert the string:

dict = { name: 'Tom', when: 'soon' }

output = ''
each_part(str) do |type, value|
  case type
  when :text
    output << value
  when :var
    output << dict[value.to_sym]
  end
end
p output
#=> "Hello Tom, hope to see you soon!"

CodePudding user response:

A very simple approach:

First, split the string on '%':

str = "%greet%! Hi there, %var_1% that can be any other %var_2% injected to the %var_3%. Nice!, goodbye)"
chunks = str.split('%')

Now we can assume given the way the problem has been specified, that every other "chunk" will be a key to replace. Iterating with the index will make that easier to figure out.

chunks.each_with_index { |c, i| chunks[i] = (i.even? ? c : dict[c.to_sym]) }.join

Result:

"Hi there! Hi there, FIRST VARIABLE that can be any other values injected to the string. Nice!, goodbye)"
  • Related