Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating array of numbers from 0 to n for positive and negative n

Tags:

arrays

ruby

Given n, I want to create an array from 0 to n:

10.make_array #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

It should also work if n is negative:

-10.make_array #=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10]

I have written this code, but I think I made it more complicated than necessary (it doesn't work for negative numbers, yet):

class Fixnum
  define_method(:make_array) do
    my_array = []
    self.times() do |count|
      self.>(0)
      my_array.push(count)
    end
    my_array.push(self)
    my_array
  end
end

Is there an easier way or short-cut to do the same thing, and any suggestions on how to handle a negative number?

like image 490
Padawan Avatar asked Mar 16 '23 23:03

Padawan


2 Answers

Use a Range:

(0 .. 10).to_a
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Handling negative numbers is not a problem:

(-10 .. -1).to_a
#=> [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]
like image 172
Yu Hao Avatar answered Apr 12 '23 23:04

Yu Hao


To support negative numbers, you could use upto and downto along with a condition:

(I'm showing a stand-alone method here instead of patching Integer)

def make_array(n)
  if n > 0
    0.upto(n).to_a
  else
    0.downto(n).to_a
  end
end

The above creates an enumerator that's converted to an array using Enumerable#to_a.

You might want to skip the intermediate object (the enumerator) by directly calling Array::new - it creates an array with the given number of elements:

n = 3
Array.new(n) #=> [nil, nil, nil]

If a block is given, it passes each element's index to the block and we are supposed to returns its value. The index is in fact just what we want, so we can simply return it:

Array.new(n) { |i| i } #=> [0, 1, 2]

As you can see, Array.new(n) returns n elements, but we need n + 1, so let's fix that:

Array.new(n + 1) { |i| i } #=> [0, 1, 2, 3]

Unfortunately, Array::new doesn't accept a negative size:

Array.new(-3) #=> negative array size (ArgumentError)

So for negative n we have to pass -n and also return -i:

n = -3
Array.new(-n + 1) { |i| -i } #=> [0, -1, -2, -3]

As a method:

def make_array(n)
  if n > 0
    Array.new(n + 1) { |i| i }
  else
    Array.new(-n + 1) { |i| -i }
  end
end

Let's try to avoid the duplication.

Converting n to a positive number is easy using abs:

3.abs  #=> 3
-3.abs #=> 3

Applied to our code:

n = 3
Array.new(n.abs + 1) { |i| i } #=> [0, 1, 2, 3]

n = -3
Array.new(n.abs + 1) { |i| i } #=> [0, 1, 2, 3]

For the block, we could use a ternary if:

n = 3
Array.new(n.abs + 1) { |i| n > 0 ? i : -i } #=> [0, 1, 2, 3]

n = -3
Array.new(n.abs + 1) { |i| n > 0 ? i : -i } #=> [0, -1, -2, -3]

We can even remove the condition by using the spaceship operator a <=> b. It determines whether a is less than, equal to, or greater than b by returning -1, 0 or +1, respectively.

More specifically, for Fixnum#<=>, n <=> 0 returns 1 if n is greater than 0, and -1 if n is less than 0:

3 <=> 0  #=> 1
-3 <=> 0 #=> -1

We can use <=>'s result in our block to multiply i with:

Array.new(n.abs + 1) { |i| i * (n <=> 0) }

This is equivalent to i * 1 (if n > 0) or i * -1 (if n < 0).

(There's a third return value: n <=> 0 returns 0 if n is equal to 0 but that doesn't matter, because in that case the resulting array is [0] and 0 * 0 is still 0)

As a method:

def make_array(n)
  Array.new(n.abs + 1) { |i| i * (n <=> 0) }
end

Although it is short, this method became quite complex and it's not obvious what is is doing. Therefore, I would prefer the first method (the one using upto and downto) because of its simplicity.

like image 33
Stefan Avatar answered Apr 12 '23 22:04

Stefan