Array of Numbers and Half Numbers in Ruby - ruby

I'm trying to populate an array with sizes that are measured in whole and half numbers (i.e. 10, 10.5, 11, 11.5, 12). So far I have:
(10..12).map{ |size| [size, size + 0.5] }.flatten[0...-1]
Does a more eloquent way of doing this exist in Ruby without having to flatten and remove the last element?

My personal favorite:
>> (10..12).step(0.5).to_a
=> [10.0, 10.5, 11.0, 11.5, 12.0]

You can use lambdas (notice that it outputs floats) - I'll let you decide if that is more eloquent.
irb(main):001:0> fn = ->(x, y) { (x*2..y*2).map { |i| i / 2.0 } }
=> #<Proc:0x007fa782b0a4b0#(irb):1 (lambda)>
irb(main):002:0> fn.call(10, 12)
=> [10.0, 10.5, 11.0, 11.5, 12.0]

I've assumed you want the values in the returned array to alternate between Fixnum and Float, as in your example:
(10..12).flat_map { |size| [size, size + 0.5] }.tap { |a| a.pop }
#=> [10, 10.5, 11, 11.5, 12]

Related

How to use a range with .. vs ... in Ruby [duplicate]

I've just started learning Ruby and Ruby on Rails and came across validation code that uses ranges:
validates_inclusion_of :age, :in => 21..99
validates_exclusion_of :age, :in => 0...21, :message => "Sorry, you must be over 21"
At first I thought the difference was in the inclusion of endpoints, but in the API docs I looked into, it didn't seem to matter whether it was .. or ...: it always included the endpoints.
However, I did some testing in irb and it seemed to indicate that .. includes both endpoints, while ... only included the lower bound but not the upper one. Is this correct?
The documentation for Range† says this:
Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
So a..b is like a <= x <= b, whereas a...b is like a <= x < b.
Note that, while to_a on a Range of integers gives a collection of integers, a Range is not a set of values, but simply a pair of start/end values:
(1..5).include?(5) #=> true
(1...5).include?(5) #=> false
(1..4).include?(4.1) #=> false
(1...5).include?(4.1) #=> true
(1..4).to_a == (1...5).to_a #=> true
(1..4) == (1...5) #=> false
†The docs used to not include this, instead requiring reading the Pickaxe’s section on Ranges. Thanks to #MarkAmery (see below) for noting this update.
That is correct.
1.9.3p0 :005 > (1...10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
1.9.3p0 :006 > (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The triple-dot syntax is less common, but is nicer than (1..10-1).to_a
The API docs now describe this behaviour:
Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
-- http://ruby-doc.org/core-2.1.3/Range.html
In other words:
2.1.3 :001 > ('a'...'d').to_a
=> ["a", "b", "c"]
2.1.3 :002 > ('a'..'d').to_a
=> ["a", "b", "c", "d"]
a...b excludes the end value, while a..b includes the end value.
When working with integers, a...b behaves as a..b-1.
>> (-1...3).to_a
=> [-1, 0, 1, 2]
>> (-1..2).to_a
=> [-1, 0, 1, 2]
>> (-1..2).to_a == (-1...3).to_a
=> true
But really the ranges differ on a real number line.
>> (-1..2) == (-1...3)
=> false
You can see this when incrementing in fractional steps.
>> (-1..2).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]
>> (-1...3).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
.. and ... denote a range.
Just see it in irb:
ruby-1.9.2-p290 :032 > (1...2).each do puts "p" end
p
=> 1...2
ruby-1.9.2-p290 :033 > (1..2).each do puts "p" end
p
p

Open ranges in Ruby using floats?

Is it possible to create ranges in ruby that exclude one or both of the endpoints. So handling the concept in mathematics of open and closed interval boundaries?
For example, can I define a range from 1.0 to 10.0 that excludes 1.0
Say (with pseudo-ruby)
range = [1.0...10.0)
range === 1.0
=> false
range === 10.0
=> true
The Range class in Ruby only supports closed and half-open (right-open) ranges. However, you can easily write your own.
Here's an example of a half-open range in Ruby:
range = 1.0...10.0
range === 1.0
# => true
range === 10.0
# => false
The total line count for the Ruby 1.9 compliant Range class in Rubinius is 238 lines of Ruby code. If you don't need your open range class to support every wrinkle, corner case, special case, idiosyncrasy, backwards-compatibility quirk and so on of the Ruby Language Specification you can get by with a lot less than that.
If you really only need to test for inclusion, then something like this should suffice:
class OpenRange
attr_reader :first, :last
def initialize(first, last, exclusion = {})
exclusion = { first: false, last: false }.merge(exclusion)
#first, #last, #first_exclusive, #last_exclusive = first, last, exclusion[:first], exclusion[:last]
end
def first_exclusive?; #first_exclusive end
def last_exclusive?; #last_exclusive end
def include?(other)
case [first_exclusive?, last_exclusive?]
when [true, true]
first < other && other < last
when [true, false]
first < other && other <= last
when [false, true]
first <= other && other < last
when [false, false]
first <= other && other <= last
end
end
alias_method :===, :include?
def to_s
"#{if first_exclusive? then '(' else '[' end}##first...##last#{if last_exclusive? then ')' else ']' end}"
end
alias_method :inspect, :to_s
end
you can exclude rightmost element of the range with .... See example below
(1..10).to_a # an array of numbers from 1 to 10 - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1...10).to_a # an array of numbers from 1 to 9 - [1, 2, 3, 4, 5, 6, 7, 8, 9]
Ranges constructed using .. run from the start to the end inclusively. Those created using ... exclude the end value.
('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a #=> ["a", "b", "c", "d"]
See more here
You could easily write your own Range to exclude the start value too.
For float ranges:
(1.0..10.0).step.to_a # => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
(1.0...10.0).step.to_a # => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
(1.0..10.0).step(2.0).to_a # => [1.0, 3.0, 5.0, 7.0, 9.0]
(1.0...10.0).step(2.0).to_a # => [1.0, 3.0, 5.0, 7.0, 9.0]
a few years later but...
what about taking advantage of the facts that a case statement evaluates each criteria in order of appearance and follows the path based on it's first match.
In the sample 1.0 always does nothing even though it's technically a valid value for the second "when".
case myvalue
when 1.0
#Don't do anything (or do something else)
when 1.0...10.0
#Do whatever you do when value is inside range
# not inclusive of either end point
when 10.0
#Do whatever when 10.0 or greater
end

Reverse `...` method Ruby

Is there a standard method in ruby similar to (1...4).to_a is [1,2,3,4] except reverse i.e. (4...1).to_a would be [4,3,2,1]?
I realize this can easily be defined via (1...4).to_a.reverse but it strikes me as odd that it is not already and 1) am I missing something? 2) if not, is there a functional/practical reason it is not already?
The easiest is probably this:
4.downto(1).to_a #=> [4, 3, 2, 1]
Alternatively you can use step:
4.step(1,-1).to_a #=> [4, 3, 2, 1]
Finally a rather obscure solution for fun:
(-4..-1).map(&:abs) #=> [4, 3, 2, 1]
(1...4) is a Range. Ranges in ruby are not like arrays; one if their advantages is you can create a range like
(1..1e9)
without taking up all of your machine's memory. Also, you can create this range:
r = (1.0...4.0)
Which means "the set of all floating point numbers from 1.0 to 4.0, including 1.0 but not 4.0"
In other words:
irb(main):013:0> r.include? 3.9999
=> true
irb(main):014:0> r.include? 3.99999999999
=> true
irb(main):015:0> r.include? 4.0
=> false
you can turn an integer Range into an array:
irb(main):022:0> (1..4).to_a
=> [1, 2, 3, 4]
but not a floating point range:
irb(main):023:0> (1.0...4.0).to_a
TypeError: can't iterate from Float
from (irb):23:in `each'
from (irb):23:in `to_a'
from (irb):23
from /home/mslade/rubygems1.9/bin/irb:12:in `<main>'
Because there is no natural way to iterate over floating point numbers. Instead you use #step:
irb(main):015:0> (1..4).step(0.5).to_a
=> [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
irb(main):016:0> (1...4).step(0.5).to_a
=> [1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
If you need to iterate backwards through a large integer range, use Integer#downto.
You could patch Range#to_a to automatically work with reverse like this:
class Range
alias :to_a_original :to_a
def reverse
Range.new(last, first)
end
def to_a
(first < last) ? to_a_original : reverse.to_a_original.reverse
end
end
Result:
(4..1).to_a
=> [4, 3, 2, 1]
This approach is called "re-opening" the class a.k.a. "monkey-patching". Some developers like this approach because it's adding helpful functionality, some dislike it because it's messing with Ruby core.)

Difference between '..' (double-dot) and '...' (triple-dot) in range generation?

I've just started learning Ruby and Ruby on Rails and came across validation code that uses ranges:
validates_inclusion_of :age, :in => 21..99
validates_exclusion_of :age, :in => 0...21, :message => "Sorry, you must be over 21"
At first I thought the difference was in the inclusion of endpoints, but in the API docs I looked into, it didn't seem to matter whether it was .. or ...: it always included the endpoints.
However, I did some testing in irb and it seemed to indicate that .. includes both endpoints, while ... only included the lower bound but not the upper one. Is this correct?
The documentation for Range† says this:
Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
So a..b is like a <= x <= b, whereas a...b is like a <= x < b.
Note that, while to_a on a Range of integers gives a collection of integers, a Range is not a set of values, but simply a pair of start/end values:
(1..5).include?(5) #=> true
(1...5).include?(5) #=> false
(1..4).include?(4.1) #=> false
(1...5).include?(4.1) #=> true
(1..4).to_a == (1...5).to_a #=> true
(1..4) == (1...5) #=> false
†The docs used to not include this, instead requiring reading the Pickaxe’s section on Ranges. Thanks to #MarkAmery (see below) for noting this update.
That is correct.
1.9.3p0 :005 > (1...10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
1.9.3p0 :006 > (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The triple-dot syntax is less common, but is nicer than (1..10-1).to_a
The API docs now describe this behaviour:
Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
-- http://ruby-doc.org/core-2.1.3/Range.html
In other words:
2.1.3 :001 > ('a'...'d').to_a
=> ["a", "b", "c"]
2.1.3 :002 > ('a'..'d').to_a
=> ["a", "b", "c", "d"]
a...b excludes the end value, while a..b includes the end value.
When working with integers, a...b behaves as a..b-1.
>> (-1...3).to_a
=> [-1, 0, 1, 2]
>> (-1..2).to_a
=> [-1, 0, 1, 2]
>> (-1..2).to_a == (-1...3).to_a
=> true
But really the ranges differ on a real number line.
>> (-1..2) == (-1...3)
=> false
You can see this when incrementing in fractional steps.
>> (-1..2).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]
>> (-1...3).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
.. and ... denote a range.
Just see it in irb:
ruby-1.9.2-p290 :032 > (1...2).each do puts "p" end
p
=> 1...2
ruby-1.9.2-p290 :033 > (1..2).each do puts "p" end
p
p

Create two-dimensional arrays and access sub-arrays in Ruby

I wonder if there's a possibility to create a two dimensional array and to quickly access any horizontal or vertical sub array in it?
I believe we can access a horizontal sub array in the following case:
x = Array.new(10) { Array.new(20) }
x[6][3..8] = 'something'
But as far as I understand, we cannot access it like this:
x[3..8][6]
How can I avoid or hack this limit?
There are some problems with 2 dimensional Arrays the way you implement them.
a= [[1,2],[3,4]]
a[0][2]= 5 # works
a[2][0]= 6 # error
Hash as Array
I prefer to use Hashes for multi dimensional Arrays
a= Hash.new
a[[1,2]]= 23
a[[5,6]]= 42
This has the advantage, that you don't have to manually create columns or rows. Inserting into hashes is almost O(1), so there is no drawback here, as long as your Hash does not become too big.
You can even set a default value for all not specified elements
a= Hash.new(0)
So now about how to get subarrays
(3..5).to_a.product([2]).collect { |index| a[index] }
[2].product((3..5).to_a).collect { |index| a[index] }
(a..b).to_a runs in O(n). Retrieving an element from an Hash is almost O(1), so the collect runs in almost O(n). There is no way to make it faster than O(n), as copying n elements always is O(n).
Hashes can have problems when they are getting too big. So I would think twice about implementing a multidimensional Array like this, if I knew my amount of data is getting big.
rows, cols = x,y # your values
grid = Array.new(rows) { Array.new(cols) }
As for accessing elements, this article is pretty good for step by step way to encapsulate an array in the way you want:
How to ruby array
You didn't state your actual goal, but maybe this can help:
require 'matrix' # bundled with Ruby
m = Matrix[
[1, 2, 3],
[4, 5, 6]
]
m.column(0) # ==> Vector[1, 4]
(and Vectors acts like arrays)
or, using a similar notation as you desire:
m.minor(0..1, 2..2) # => Matrix[[3], [6]]
Here's a 3D array case
class Array3D
def initialize(d1,d2,d3)
#data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
end
def [](x, y, z)
#data[x][y][z]
end
def []=(x, y, z, value)
#data[x][y][z] = value
end
end
You can access subsections of each array just like any other Ruby array.
#data[0..2][3..5][8..10] = 0
etc
x.transpose[6][3..8] or x[3..8].map {|r| r [6]} would give what you want.
Example:
a = [ [1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25]
]
#a[1..2][2] -> [8,13]
puts a.transpose[2][1..2].inspect # [8,13]
puts a[1..2].map {|r| r[2]}.inspect # [8,13]
I'm quite sure this can be very simple
2.0.0p247 :032 > list = Array.new(5)
=> [nil, nil, nil, nil, nil]
2.0.0p247 :033 > list.map!{ |x| x = [0] }
=> [[0], [0], [0], [0], [0]]
2.0.0p247 :034 > list[0][0]
=> 0
a = Array.new(Array.new(4))
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
a[i[j]] = 1
end
end
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
print a[i[j]] = 1 #It's not a[i][j], but a[i[j]]
end
puts "\n"
end
Here is the simple version
#one
a = [[0]*10]*10
#two
row, col = 10, 10
a = [[0]*row]*col
Here is an easy way to create a "2D" array.
2.1.1 :004 > m=Array.new(3,Array.new(3,true))
=> [[true, true, true], [true, true, true], [true, true, true]]

Resources