Why is the delimiter still iterating on the last array item?

The challenge here is to create a custom .join method. When I use the .each method, the delimiter iterates over the last array item. So I used .each_with_index, and in the given if statement, I specified that the last item in the array should NOT get the delimiter. Why is is still there?

def custom_join(array, delimiter = “”)
names_2 = “”
array.each_with_index do |name, index|
if array.index == -1
names_2 << name
else
names_2 << name << delimiter
end

end
names_2
end

print custom_join(names, “__”)

Because inside the block, the value of index is never equal to -1. Add a few puts statements in to print out the variables you are testing so you can see what’s going on. The simplest way to do this (and quite likely what happens under the covers of join) is:

ary = %w(a b c d e)

def custom_join(ary, delim = '')
  ary.reduce { |memo, obj| memo + delim + obj }
end

puts custom_join ary, 'x'

When called without an initialising value for memo, the default action of reduce is to use the first array element. So this turns the problem on its head - we start with item 0 on its own, and items 1 through n get prefixed with the delimiter. So you avoid the problem of identifying the last item in the array.

The solution that @specious wrote works absolutely great. But here’s my solution, just to elaborate your problem:

#!/usr/bin/env ruby

def custom_join(array, delimiter = '')
	names_2 = ''
	array[0..-2].each { |name| names_2 << name << delimiter }
	names_2 << array[-1]
end

ary = %w(a b c d e)
p custom_join(ary, ',')

What I did is, instead of having array indices, and checking if the index is array.length - 1, I am iterating without including the last item. It also works fine.

Similarly:

def custom_join(array, delimiter = '')
	array[0..-2].reduce('') { |n, name| n << name << delimiter } << array[-1]
end

ary = %w(a b c d e)
p custom_join(ary, ',')

But it has a huge problem. When you write array[0…-2], Ruby creates a copy of the whole array without the last item. So, on a 100 MB array, it will copy the whole array but the last item, the GC will discard it after the string is created (at some point that I can’t say).

@specious solution won’t have this issue at least…


I will say you just use the .join method or other stuff that ruby already has in the standard library. It’s great to replicate these methods to learn, but they are not great in production environment. Even if you replicate the Ruby’s Array#sort and write in Ruby, it will be slower than the Array#sort. Because most of them are written in C, and they tend to work way faster than custom methods. But sure, it’s good to replicate such stuff to learn Ruby…