5
\$\begingroup\$

We'll say that a lowercase 'g' in a string is "happy" if there is another 'g' immediately to its left or right. Return true if all the g's in the given string are happy.

gHappy("xxggxx") → true
gHappy("xxgxx") → false
gHappy("xxggyygxx") → false

I have written two functions to solve this problem, but I have some concerns:

  • Of the one with Regex, I don't like all the special cases.
  • Of the one with iteration, I don't like the weird helper method.

Both pass a fair number of tests and work correctly. Which one is better?

regex_geppy.rb

def geppy(string)
  return true if ! string.include?('g')
  return false if [0, 1].include?(string.length)
  return false if string.length == 2 && string != 'gg'
  return (not string.match("[^g]g[^g]"))
end

array_geppy.rb

def non_wrap_get(array, index)
  index < 0 ? nil : array[index]
end

def geppy(string)
  string
    .chars
    .each_with_index
    .all? { |curr, i| 
      curr == 'g' ? 
        non_wrap_get(string, i - 1) == 'g'  || non_wrap_get(string, i + 1) == 'g' 
        : true
    }
end

geppy_tests

geppy("xxggxx") #=> true
geppy("xxgxx") #=> false
geppy("xxggyygxx") #=> false
geppy("g") #=> false
geppy("ag") #=> false
geppy("gg") #=> true
geppy("") #=> true
geppy("ab") #=> true
geppy("aaaa") #=> true
geppy('g'*1000) #=> true
\$\endgroup\$
2
\$\begingroup\$

@200_success already showed a compact solution using regular expressions. As for the "array-solution", I think you missed the abstraction Enumerable#chunk:

def g_happy(s)
  s.chars.chunk { |c| c }.all? { |c, cs| c == "g" ? cs.size > 1 : true } 
end

Or .all? { |c, cs| cs != ["g"] }, whatever feels more declarative to you.

\$\endgroup\$
  • \$\begingroup\$ Error: undefined method "itself" for string which version are you using? \$\endgroup\$ – Caridorc Jul 11 '15 at 9:07
  • 1
    \$\begingroup\$ >= 2.2.0. Changed to a more portable block. \$\endgroup\$ – tokland Jul 11 '15 at 11:37
2
\$\begingroup\$

Regular expressions are the right tool to use for this kind of test. However, if you are going to use a regular expression to find lonely g characters, then you should use negative lookbehind and lookahead assertions instead of the negated character class [^g].

def geppy(str)
  !/(?<!g)g(?!g)/.match(str)
end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.