0

I learned Rails and now would like to expand my knowledge of Ruby. So I'm doing some C++ exercises in Ruby. Specifically I need to find if a substring exists in a string. If it does I need it to return its starting index. If it doesn't exist have it return -1. I came up with a Ruby solution that's very similar to C++ and was wondering if there's a "better", more idiomatic solution in Ruby?

C++

int find(char str[], char sub_str[])
{
  int str_length = strlen(str);
  int sub_str_length = strlen(sub_str);
  bool match = false;

  for(int i=0; i<str_length; i++)
  {
    if(str[i] == sub_str[0])
    {
      for(int j=1; j<sub_str_length; j++)
      {
        if(str[i+j] == sub_str[j])
          match = true;
        else
        {
          match = false;
          break;
        }
      }
      if(match)
        return i;
    }
  }
  return -1;
}

Ruby

def find_sub_str(str, sub_str)
  match = false
  for i in 0...str.length
    if str[i] == sub_str[0]
      for j in 1...sub_str.length
        if str[i+j] == sub_str[j]
          match = true
        else
          match = false
          break
        end
      end
      if match == true
        return i
      end
    end
  end
  return -1
end
10
  • Ruby implements many generic String, Array routines of this sort directly as methods on the built-in classes. To learn idiomatic Ruby you will want to stretch out into some higher-level business logic - e.g. a routine that used this string matching to provide highlighting of terms in HTML. Commented Jul 15, 2013 at 21:07
  • 4
    Best bet is to pretend you don't know any C++, it will keep leading you astray. Commented Jul 15, 2013 at 21:08
  • 1
    You learned Rails but don't know Ruby? Then you don't know Rails, you only know how to configure parts of it. Commented Jul 15, 2013 at 21:12
  • 1
    Without Ruby there is no Rails. Rails, without an understanding of Ruby is a very steep learning curve, and is restricted to merely configuring things, not programming controllers, writing views, or defining the actions of models. Even the DSLs within Rails require a modicum of Ruby otherwise the syntax will make no sense, nor will using blocks. Once someone starts to understand Ruby they can actually become creative and productive. Without Ruby someone merely parrots what others have done. Commented Jul 15, 2013 at 21:25
  • 2
    I'd highly recommend NOT try to learn Ruby, a fairly high-level language, based on C++ exercises, which teach a fairly low-level language. C and C++ are closer to assembler than they are Ruby; Learning Ruby from Perl would be a closer match. I've written in them all, and the progression of the functionality of each language and how we think in them, leads to quite a gulf with C++ <==> Ruby. If you want to try that, I'd recommend thinking beyond searching for characters in strings by indexing -- that's assembler thinking and concentrate on higher functions. That's the gist of Andrew's answer. Commented Jul 15, 2013 at 21:36

3 Answers 3

4

You can use the index method of String. It returns nil on failure to match, which is more idiomatic Ruby than returning -1.

 "SubString".index("String") # -> 3
 "SubString".index("C++") # -> nil

You could wrap it in a test that returns -1 for nil if you really wanted this behavior.

Sign up to request clarification or add additional context in comments.

2 Comments

Really, really want it.
i detect c++ bashing, even though we have auto index = s1.find(s2); and in c++14 "Substring"s.find("string"); - which , before you ask, gives -1 on failure.
2
  1. Don’t use for in Ruby, it just calls each and doesn’t introduce scope. So for i in 0...str.length becomes (0...str.length).each do |i|.

  2. Higher-order functions are your friend! Using each_cons & find_index makes things much cleaner (study Enumerable, it’s home to many useful methods):

    def find_sub_str(str, sub_str)
      str.chars.each_cons(sub_str.length).find_index do |s|
        s.join == sub_str
      end
    end
    
    find_sub_str('foobar', 'ob')  #=> 2
    
  3. Just use Ruby core’s index :):

    'foobar'.index('ob')  #=> 2
    

Both #2 & #3 return nil, not -1, when there is no match. This is best because nil is falsey in Ruby.

1 Comment

+1. Also, using each helps sidestep the "Help! I've run off the end of the array" problems that indexing encounters too.
0
#how if we use this solution, it gets the job done in O(n)    

given_string = "Replace me with your code!"

chars_given_string = given.split('')

chars_of_substr = "ith".split('')

is_substr = false
ptr = 0

char_given.each do |i|
    if ( i == substr[ptr])
       ptr += 1
    else
       ptr = 0
    end
    is_substr = true if ptr == substr.length
    break if ptr == substr.length
end

puts is_substr

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.