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')


• 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

# ["a", "ab", "abc"]

• Oh, cool. Ok, thank you. And yes you are correct, it was leading substrings, not all the substrings. Oct 7, 2017 at 2:10

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"]

• 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. Oct 5, 2017 at 12:02
• 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. Oct 5, 2017 at 15:09
• 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. Oct 7, 2017 at 2:11
• Sure, do whatever you like with it :) Oct 7, 2017 at 4:25

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

• Opening the String class is a very bad idea, known as monkey patching. One should never do this. Oct 3, 2017 at 5:58
• 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. Oct 3, 2017 at 10:36
• Oh, OK. Then the Ruby people must be very disciplined. Oct 3, 2017 at 14:40