Home > database >  Ruby code to iterate over every n-th element of an array and print it until all elements are printed
Ruby code to iterate over every n-th element of an array and print it until all elements are printed

Time:01-29

I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.

The question reads: Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning. For example:

x = [0,1,2,3,4]
x.stride(1) do |elem|; puts elem; end # prints 0,1,2,3,4
x.stride(2) do |elem|; puts elem; end # prints 0,2,4,1,3
x.stride(8) do |elem|; puts elem; end # prints 0,3,1,4,2
[].stride(2) do |elem|; puts elem; end # does not print anything, but the code is correct

Assume that the stride is equal or greater than 1, and that both the stride and the array's size are not a integral/whole multiple of each other, meaning that the whole array can be printed using a given stride. Fill in the code that's missing:

class Array
    def stride(step)
        numelems = ... # size of the array
        ...
    end
end

It is obvious that numelemns = self.length(). However am having trouble with the rest. I am going to try writing some code in Python that accomplishes this task, but I am afraid that I will not be able to translate it to Ruby.

Any ideas? The answer should not be more than 4-5 lines long as the question is one that our proffessor gave us to solve in a couple of minutes.


A solution to this is provided below (thanks @user3574603):

class Array
  def stride(step)
      yield self[0]
      (self * step).map.with_index do |element, index|
          next element if index == 0
          yield element if index % step == 0
    end
  end
end

CodePudding user response:

This almost does what I think you want but breaks if the step is array.length 1 array.length (but you mention that we should assume the stride is not a multiply of the array length).

class Array
  def exhaustive_stride(step)
    (self * step).map.with_index do |element, index|
      next element if index == 0

      element if index % step == 0
    end.compact
  end
end
x.exhaustive_stride 1
#=> [0, 1, 2, 3, 4] 
x.exhaustive_stride 2
#=> [0, 2, 4, 1, 3] 
x.exhaustive_stride 8
#=> [0, 3, 1, 4, 2] 
[].exhaustive_stride 2
#=> []

Using the example array, it breaks when the stride is 5.

[0,1,2,3,4].exhaustive_stride 5
#=> [0, 0, 0, 0, 0]

CodePudding user response:

def striding(arr, n)
  sz = arr.size
  raise ArgumentError,
    "invalid as arr.size = #{arr.size} is a multiple of n = #{n}" if
    (sz % n).zero? || (arr.size.even? && n.even)
  covered = Array.new(sz, false)
  nbr_uncovered = sz
  i = -n
  while nbr_uncovered > 0
    j = (i  = n) % sz
    puts "S".rjust(j 1)
    unless covered[j]
      covered[j] = true
      nbr_uncovered -= 1
    end
    display_covered(covered)
  end
end
def display_covered(covered)
  covered.each { |c| print c ? 'X' : 'O' }
  puts
end 

striding [1,2,3,4], 3
S
XOOO
   S
XOOX
  S
XOXX
 S
XXXX
striding [1,2,3,4,5,6,7,8,9,1,2,3,4,5], 11
S
XOOOOOOOOOOOOO
           S
XOOOOOOOOOOXOO
        S
XOOOOOOOXOOXOO
     S
XOOOOXOOXOOXOO
  S
XOXOOXOOXOOXOO
             S
XOXOOXOOXOOXOX
          S
XOXOOXOOXOXXOX
       S
XOXOOXOXXOXXOX
    S
XOXOXXOXXOXXOX
 S
XXXOXXOXXOXXOX
            S
XXXOXXOXXOXXXX
         S
XXXOXXOXXXXXXX
      S
XXXOXXXXXXXXXX
   S
XXXXXXXXXXXXXX
striding [1,2,3,4,5,6,7,8,9,1,2,3,4,5], 7
  #=> ArgumentError: invalid as arr.size = 14 is a multiple of n 

CodePudding user response:

Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning.

Based on this specification, stride will always iterate forever, unless the array is empty. But that is not a problem, since we can easily take only the amount of elements we need.

In fact, that is a good design: producing an infinite stream of values lets the consumer decide how many they need.

A simple solution could look like this:

module EnumerableWithStride
  def stride(step = 1)
    return enum_for(__callee__, step) unless block_given?
    return self if length.zero?

    enum = cycle

    loop do
      yield enum.next
      (step - 1).times { enum.next }
    end
  end
end

Enumerable.include(EnumerableWithStride)

A couple of things to note here:

I chose to add the stride method to Enumerable instead of Array. Enumerable is Ruby's work horse for iteration and there is nothing in the stride method that requires self to be an Array. Enumerable is simply the better place for it.

Instead of directly monkey-patching Enumerable, I put the method in a separate module. That makes it easier to debug code for others. If they see a stride method they don't recognize, and inspect the inheritance chain of the object, they will immediately see a module named EnumerableWithStride in the inheritance chain and can make the reasonable assumption that the method is probably coming from here.

For an empty array, nothing happens:

[].stride(2, &method(:p))
#=> []

stride just returns self (just like each does) and the block is never executed.

For a non-empty array, we get an infinite stream of values:

x.stride(&method(:p))
# 0
# 1
# 2
# 3
# 4
# 0
# 1
# …

x.stride(2, &method(:p))
# 0
# 2
# 4
# 1
# 3
# 0
# 2
# …

x.stride(8, &method(:p))
# 0
# 3
# 1
# 4
# 2
# 0
# 3
# …

The nice thing about this infinite stream of values is that we, as the consumer can freely choose how many elements we want. For example, if I want 10 elements, I simply take 10 elements:

x.stride(3).take(10)
#=> [0, 3, 1, 4, 2, 0, 3, 1, 4, 2]

This works because, like all well-behaved iterators, our stride method returns an Enumerator in case no block is supplied:

enum = x.stride(2)
#=> #<Enumerator: ...>

enum.next
#=> 0

enum.next
#=> 2

enum.next
#=> 4

enum.next
#=> 1

enum.next
#=> 3

enum.next
#=> 0

enum.next
#=> 2

So, if we want to implement the requirement "until all the elements of the array are printed":

I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.

We could implement that something like this:

x.stride.take(x.length).each(&method(:p))
x.stride(2).take(x.length).each(&method(:p))
x.stride(8).take(x.length).each(&method(:p))

This is a pretty simplistic implementation, though. Here, we simply print as many elements as there are elements in the original array.

We could implement a more sophisticated logic using Enumerable#take_while that keeps track of which elements have been printed and which haven't, and only stops if all elements are printed. But we can easily prove that after x.length iterations either all elements have been printed or there will never be all elements printed (if the stride size is an integral multiple of the array length or vice versa). So, this should be fine.

  •  Tags:  
  • ruby
  • Related