Home > other >  Simply Question on Arrays in Ruby - Extra Array Element Upon Creation and When Working With Array Da
Simply Question on Arrays in Ruby - Extra Array Element Upon Creation and When Working With Array Da

Time:12-27

# For example, the base [1, 4, 6] gives us the following pyramid
#     15
#   5   10
# 1   4    6

def pyramid_sum(base)
  pyramid = [base]
  new_level = []
  prev_level = []
  base.length.times { |x|
    prev_level = pyramid[0]
    new_level = build(prev_level)
    pyramid.unshift(new_level)
  } 
  return pyramid
end

def build(level)
  new_level = []
  level.each_with_index { |num, index|
    if index < level.length-1
      new_level <<level[index]   level[index 1]
    end
  }
  return new_level
end

print pyramid_sum([1, 4, 6]) #=> [[15], [5, 10], [1, 4, 6]]
puts

print pyramid_sum([3, 7, 2, 11]) #=> [[41], [19, 22], [10, 9, 13], [3, 7, 2, 11]]
puts

Outputs: ** [[], [15], [5, 10], [1, 4, 6]] [[], [41], [19, 22], [10, 9, 13], [3, 7, 2, 11]] **

Why is there always an extra [] (empty array) in the front of the 2D array? I have seen this many times in my results with arrays in Ruby and though it is simple I can't seem to figure out why that extra annoying array is always there? I KNOW this is simple and I may delete this question if it warrants such but why is there an extra array element in the front of my array? It seems like to merely create an array is it always have an extra element in it and I can't "truly" modify it because everything in Ruby is an object anyways.

CodePudding user response:

The reason why you get that extra empty array at the start is because you tell the pyramid_sum function to add base.length more levels to the base.

A pyramid made up in this fashion will have as many levels as it has items in its base.

So that means that when you write base.length.times { |x| ... }, you are essentially saying that you will be adding base.length new extra levels on top of the base level.

For your first example, [1, 4, 6], this means that you say that on top of this base layer, you will add three more layers:

  • First, you will sum up the base layer in parts, adding [5, 10]
  • Second, you will sum up the new, second layer in parts, adding [15]
  • Lastly, you will attempt to sum up that third layer in parts, and this will result in the []

The reason is that when you try to sum up that third layer, there is not enough items in it to add any two numbers from the layer below. Therefore, you will end up adding the empty new_layer = [] that you haven't modified.

If you want to fix this, you can either run the iteration (base.length - 1).times, or you can add an if statement as a guard to not add any generated empty layers:

new_level = build(prev_layer)
if new_level.length > 0
  pyramid.unshift(new_level)
end

CodePudding user response:

As a first step, I have reformatted your code a bit and removed some useless code as well. (For example, the assignments to new_level and prev_level in pyramid_sum are useless, since they are immediately re-assigned in the block anyway.)

def pyramid_sum(base)
  pyramid = [base]

  base.length.times do
    prev_level = pyramid.first
    new_level = build(prev_level)
    pyramid.unshift(new_level)
  end

  pyramid
end

def build(level)
  new_level = []

  level.each_index do |index|
    if index < level.length - 1
      new_level << level[index]   level[index   1]
    end
  end

  new_level
end

With those useless assignments removed, the two lines that create the problem are right below each other, and the problem pretty much already jumps out:

  • The height of your pyramid should always be the same as the width of the base.
  • You pre-initialize the result variable with the original base as the first level of the pyramid.
  • But then you iterate the build method base.length times, i.e. in addition to the one level you have pre-initialized, you add another base.length levels … and as a result, you end up with one level too many: you build as many levels as you need but you had already put another level into the result.

The solution for that is simple: iterate base.length - 1 times:

def pyramid_sum(base)
  pyramid = [base]

  (base.length-1).times do
    prev_level = pyramid.first
    new_level = build(prev_level)
    pyramid.unshift(new_level)
  end

  pyramid
end

def build(level)
  new_level = []

  level.each_index do |index|
    if index < level.length - 1
      new_level << level[index]   level[index   1]
    end
  end

  new_level
end

Note that this problem almost screams for recursion. Something like this:

def pyramid_sum(*base) = pyramid_rec([base]).reverse

def pyramid_rec(pyramid)
  return pyramid if pyramid.last.size <= 1

  pyramid_rec(pyramid << pyramid.last.each_cons(2).map(&:sum))
end

puts pyramid_sum(1, 4, 6)
puts pyramid_sum(3, 7, 2, 11)
  • Related