2
\$\begingroup\$

I have a couple of answers that I wrote up for this exercise. Both of my methods work. I'm hoping to get some feedback on each and how I could improve them, if needed.

The idea is to take a string, such as 'abc' and return an array like this: ['a', 'ab', 'abc']

For the second method, I used #map, but I'm not sure if this is the best way to achieve my desired result.

def substrings(string)
  subbed = []
  (0..string.length-1).each do |i|
    subbed.push(string.slice(0..i))
  end
  return subbed
end

def substrings2(string)
  string.split('').map.with_index do |c,i|
    string.slice(0..i)
  end
end


print substrings2('abc')
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$
  • In Ruby, there are enough Enumerable and Array methods that you virtually never need to initialize an empty array and iterate to push values.
  • Your method doesn't return every substring in the string. 'bc' isn't present for example. Your method shouldn't be called substrings. Your title suggests leading_substrings.

Here's a clear, short way to initialize this array with Array.new :

def leading_substrings(string)
  Array.new(string.size) { |i| string[0..i] }
end

p leading_substrings('abc')
# ["a", "ab", "abc"]
\$\endgroup\$
1
  • \$\begingroup\$ Oh, cool. Ok, thank you. And yes you are correct, it was leading substrings, not all the substrings. \$\endgroup\$
    – Nathan
    Commented Oct 7, 2017 at 2:10
4
\$\begingroup\$

What you're looking for is known as a "scan" in functional programming. Surprisingly, ruby's Enumerable, which is where this method belongs (since what you're doing is not, ultimately, specific to strings), does not provide a scan method or its equivalent.

But we can write one easily enough:

module Enumerable
  def scans
    reduce([]) {|m,x| m << (m.last || []) + [x]}
  end  
end

Now we have a general purpose method which we can use like so:

[9] pry(main)> (1..5).to_a.scans
=> [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]

Your original problem now becomes a simple delegation to scans:

[14] pry(main)> 'abc'.split('').scans.map(&:join)
=> ["a", "ab", "abc"]
\$\endgroup\$
4
  • \$\begingroup\$ It's a great idea if Enumerable#scans is used in other places with other objects than strings. Otherwise, it's a bit of an overkill, with many temp objects and methods. \$\endgroup\$ Commented Oct 5, 2017 at 12:02
  • 1
    \$\begingroup\$ Yes, if you're just trying to solve the given problem, it's overkill. The point is that if you're a functional programmer, you recognize this immediately as a scan, and it just happens to be a deficiency of Enumerable that it lacks a scan method. So i just created the method I should have had anyway, and now I have a clean 1 line solution, albeit not the most performant one. Fwiw, for just the problem at hand, I do prefer your solution. \$\endgroup\$
    – Jonah
    Commented Oct 5, 2017 at 15:09
  • \$\begingroup\$ This is great. I'm still new to programming, so got a lot to learn before I'M able to switch careers. @Jonah is it ok if I copy this? I need to pick it apart and learn how it works on a deeper level. \$\endgroup\$
    – Nathan
    Commented Oct 7, 2017 at 2:11
  • \$\begingroup\$ Sure, do whatever you like with it :) \$\endgroup\$
    – Jonah
    Commented Oct 7, 2017 at 4:25
2
\$\begingroup\$

Your second method is closer to idiomatic Ruby. In general, you should never need to initialize an array. chars is a nice alternative to split(''). And, you can open String and add a new method. I would do:

class String
  def substrings
    chars.map.with_index { |_char, index| slice(0..index) }
  end
end

Then:

'abc'.substrings
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Opening the String class is a very bad idea, known as monkey patching. One should never do this. \$\endgroup\$ Commented Oct 3, 2017 at 5:58
  • \$\begingroup\$ This is not correct. Monkey patching a class with a related method is considered normal practice in Ruby (and is prevalent throughout Rails). I generally recommend using refinements to achieve the same thing, but it is more complex than needed for this example. \$\endgroup\$
    – Dan Kohn
    Commented Oct 3, 2017 at 10:36
  • \$\begingroup\$ Oh, OK. Then the Ruby people must be very disciplined. \$\endgroup\$ Commented Oct 3, 2017 at 14:40

Your Answer

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

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