Is there some kind of unseen Array termination in Ruby? - ruby

I am writing an method that takes variable lengths of arrays and compresses and converts them to matching array lengths.
The method accepts an array length of anywhere from 50 to 126 . .. and converts them down based on parameters when called. This means is I am dynamically accessing ranges that are specified at method call. This also means I will be accessing indices of the array that won't exist. In addition to this, the range I am accessing may be equal. This is where my problem lies: it appears that the terminating pocket of an array behaves strangely when accessing it with a range, as opposed to any other non-existent index
>> a = [0, 1, 2, 3, 4]
# => [0, 1, 2, 3, 4]
>> a[5]
# => nil
>> a[4..4]
# => [4]
>> a[6..6]
# => nil
>> a[5..5]
# => [] <------GGRAAAAAhhh!!! Thought you were nil
>> a[5..7]
# => [] <-------you too
>> a[6..7]
# => nil <-----but you are behaving
Does anyone know why this happens? Its an extra test against an empty array for my code, but I am more curious after chasing this bug down in my rails app all day. FWIW I am running Ruby 1.8.6 on Mac OS X, patchlevel 114

Seems strange but it's a documented behaviour:
http://www.ruby-doc.org/core/classes/Array.html#M002183

The out-of range range selection is a special case, and works as per the docs:
http://www.ruby-doc.org/core/classes/Array.html#M002183
Though I'd agree that it's not exactly obvious.

It's a feature of Ruby Arrays. Only happens if you're taking a subset of an array, the first endpoint of which is equal to the length.
It allows you to do things like this:
a = [1,2,3]
a[3..3] = [4,5] # a = [1,2,3,4,5]
EDIT: Actually, you can do that with any range. My bad.

Related

How to display an array with ruby nested each?

Given a list of array, I want to display all pair combination of the array in the form of array of two. For example, let's say I have an array of [1,2,3].
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]
Expected result
This is the function I came up with:
def all_combination(list)
list.each do |i|
list.each do |j|
puts [i,j]
end
end
end
arr1 = [1,2,3] #arr1 can be an array of any size. It will always be integer.
all_combination(arr1)
However, when I do the above code, it displays
1
1
1
2
1
3
...
It seems like there is a gap in my fundamental ruby knowledge. Why puts [i,j] does not work? How can I make it display an actual array like it shows on Expected result section?
I can make it work with puts "#{i}, #{j}", but it does not return an array pair [a,b]
There's a builtin method for what you want:
[1,2,3].permutation(2).each {|a| p a}
If you prefer to stick with your own method (to get "pairs" of items with themselves), change puts [i,j] to either p [i,j] or to puts [i,j].to_s— puts works with strings.
The reason you get the data on each new line is because puts writes each object given to it on a new line. Here is the doc on this:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line. If called without arguments, outputs a single
record separator.
If Array#permutation does what you want than that is the way to go, however, I don't think it matches what you asked for (ie [1,1],[2,2],[3,3] will not be included).
The following should provide you with what you were originally asking for.
def all_combination(list)
a = []
list.each do |i|
list.each do |j|
a << [i,j]
end
end
a
end
x = all_combination [1,2,3]
p x
puts x
=> [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
1
1
1
2
1
3
2
1
2
2
2
3
3
1
3
2
3
3
x is the returned value you were looking for as shown by p x
ps: this could probably be done with a line or too less using Array#reduce, but this is fairly strait forward.
It seems like there is a gap in my fundamental ruby knowledge. Why puts [i,j] does not work?
Why don't we just look at the documentation?
The documentation for Kernel#puts simply refers us to the documentation for IO#puts:
puts(obj, …) → nil
Equivalent to
$stdout.puts(obj, …)
The documentation for IO#puts says this (bold emphasis mine):
puts(obj, …) → nil
Writes the given objects to ios as with IO#print. Writes a record separator (typically a newline) after any that do not already end with a newline sequence. If called with an array argument, writes each element on a new line. If called without arguments, outputs a single record separator.
As you can see, the documentation clearly states that when you pass an Array to puts, it will print each element on a separate line.
How can I make it display an actual array like it shows on Expected result section?
You cannot display an "actual array". An array is a data structure that maps indices to objects. It's an idea. You cannot display an idea. You can only display text (i.e. a String). So, you need to construct a String that looks like your desired output.
I can make it work with puts "#{i}, #{j}", but it does not return an array pair [a,b]
Well, it doesn't print the brackets because you didn't tell it to print brackets. It will simply print your String, but there aren't any brackets in your String, ergo there won't be brackets in the output. You just have to add them:
puts "[#{i}, #{j}]"
Note: you may be tempted to use Array#inspect, Array#to_s, or Kernel#p, but please don't: their output format is not guaranteed. You may get what you expect, but then again, you may not.
#inspect is for human-readable debugging output. Kernel#p simply calls #inspect. In the particular case of Array#to_s, it is simply an alias for Array#inspect, and the documentation only says:
inspect → string
to_s → string
Creates a string representation of self.
[ "a", "b", "c" ].to_s #=> "[\"a\", \"b\", \"c\"]"
Note that it only says "a string representation", but does not guarantee what string representation it returns.
The way I would approach your problem is something like this:
def all_combination(list)
list.product(list).map {|a, b| "[#{a}, #{b}]" }
end
arr1 = [1,2,3]
puts all_combination(arr1)
I mainly did two things:
Use higher-level collection operations already provided by the Ruby core library (Array#product and Enumerable#map). This reduces the cognitive overhead of understanding the solution. It's now just one line that clearly tells you what it does: first generate the cartesian product of the list with itself, then transform each pair to a suitable string representation.
Separate construction of the output from printing the output. This makes it easier to test: you can simply assert that returns the correct value, instead of having it print directly to the screen, and then jumping through hoops to capture the screen output and compare it to some pre-recorded screen output or something like that.
There are still things to improve, though:
the method does two things: generating the cartesian product and transform it to a string representation
the method naming: this actually hints at #1 above: how can you find a suitable name that describes precisely what a method does, when the method does multiple things at once?

Ruby: Range is empty, but slicing with it produces elements

I'm learning Ruby and have just gotten into some stuff about arrays and ranges. I've run into something about slices that, while it makes sense at first glance, confuses me a bit when I look deeper into it.
IRB says that (2..-1).to_a is an empty array, meaning no values in the range, right?
But if I use that same range in [:a, :b, :c, :d, :e][2..-1], I get back [:c, :d, :e] rather than an empty array.
Now, I'm aware that -1 represents the last element of the array, so it kind of makes sense that what got picked, did. But if the range itself would be empty, how is it selecting anything?
This is a fascinating question. The answer is that it's not the individual elements of the range that are inspected when slicing the array, but the first and last elements. Specifically:
>> (2..-1).to_a
=> []
>> (2..-1).first
=> 2
>> (2..-1).last
=> -1
Thus the example works, since it slices the array from the [2] element to the [-1] element.
If you want a consistent way to think about this, consider that (2..-1).to_a outputs the integers found between 2 and -1 (of which there are none), but that [2..-1] means from the 2 index to the -1 index.
(Source: array.c and range.c in the Ruby source.)
And, the complicated bonus part: to get the meaning you were thinking about, you could use
>> [:a, :b, :c, :d, :e].values_at *(2..-1).to_a
=> []
In the slicing operation it's not seeing the range as a Range per se, it's just looking at the values of the endpoints, in this case 2 and -1. Since -1 has a special meaning (i.e last item in the list) it just returns everything from item at index 2 to the end of the list. Don't think of it as a Range here, just think of it as a convenient way of passing in two numbers (expressing two end points).
The Array#[] method does not use the range as a range (that is, it does not call include? on it or anything like that); it just takes the two numbers out of it and checks whether it's exclusive.
You can imagine it to look somewhat like this (though the real [] is implemented in C, not in Ruby, and of course also handles arguments other than ranges):
def [](range)
start = range.begin
length = range.end - start
length -= 1 if range.exclude_end?
slice(start, length)
end

Behaviour of Array bang methods

Some bang version of Array methods are like compact!, reject!, flatten!, uniq! return nil if no changes were made:
[1,[2]].flatten!
# => [1, 2]
[1,2].flatten!
# => nil
[1,[2]].flatten
# => [1, 2]
[1,2].flatten
# => [1, 2]
[1,2,nil].compact!
# => [1, 2]
[1,2].compact!
# => nil
[1,2,nil].compact
# => [1, 2]
[1,2].compact
# => [1, 2]
If they did it this way, there has to be a reason. Any ideas what it might be?
The bang (!) methods do modify the current object in place, but they do return nil if there are no affected elements per the documentation. This is useful if, for whatever reason, you need to do something if you did modify the array in question.
if array.flatten!
puts "Oh yeah... flattened that array!"
end
I was always under impression that
bang version of Array methods are
only different in the way that they
modify object in place.
Perhaps the problem here is that this impression is not really a correct one: according to David A. Black, ! does not mean that the method changes its receiver; ! means that this method is the "dangerous" version of an otherwise equivalent method, which has the same name minus the !.
Now danger takes many forms (emphasis mine):
Sometimes you get more than one kind
of "danger" even within one bang
method. Take String#gsub!. This
method changes its receiver:
str = "David"
str.gsub!(/$/, " Black")
str # David Black
It also differs from gsub (non-bang)
in that if the string does not change,
gsub returns a copy of the unchanged
string but gsub! returns nil:
str.gsub(/xyz/, "blah") # David Black
str.gsub!(/xyz/, "blah") # nil
str # David Black
The ! in gsub! gives you a heads-up:
it warns you of danger, and that means
that before you use the method, you
should find out exactly how it
behaves. (A simple "ri String#gsub!"
should do it.)
This "heads-up" semantics also applies to the bang methods of Array.

How can I get a lazy array in Ruby?

How can I get a lazy array in Ruby?
In Haskell, I can talk about [1..], which is an infinite list, lazily generated as needed. I can also do things like iterate (+2) 0, which applies whatever function I give it to generate a lazy list. In this case, it would give me all even numbers.
I'm sure I can do such things in Ruby, but can't seem to work out how.
With Ruby 1.9 you can use the Enumerator class. This is an example from the docs:
fib = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Also, this is a nice trick:
Infinity = 1.0/0
range = 5..Infinity
p range.take(10) #=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
This one only works for consecutive values though.
Recently Enumerable::Lazy has been added to ruby trunk. We'll see it in ruby 2.0.
In particular:
a = data.lazy.map(&:split).map(&:reverse)
will not be evaluated immediately.
The result is instance of Enumerable::Lazy, that can be lazy chained any further. If you want to get an actual result - use #to_a, #take(n) (#take is now lazy too, use #to_a or #force), etc.
If you want more on this topic and my C patch - see my blog post Ruby 2.0 Enumerable::Lazy
Lazy range (natural numbers):
Inf = 1.0/0.0
(1..Inf).take(3) #=> [1, 2, 3]
Lazy range (even numbers):
(0..Inf).step(2).take(5) #=> [0, 2, 4, 6, 8]
Note, you can also extend Enumerable with some methods to make working with lazy ranges (and so on) more convenient:
module Enumerable
def lazy_select
Enumerator.new do |yielder|
each do |obj|
yielder.yield(obj) if yield(obj)
end
end
end
end
# first 4 even numbers
(1..Inf).lazy_select { |v| v.even? }.take(4)
output:
[2, 4, 6, 8]
More info here:
http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/
There are also implementations of lazy_map, and lazy_select for the Enumeratorclass that can be found here:
http://www.michaelharrison.ws/weblog/?p=163
In Ruby 2.0.0, they were introduced new method "Lazy" in Enumerable class.
You can check the lazy function core and usage here..
http://www.ruby-doc.org/core-2.0/Enumerator/Lazy.html
https://github.com/yhara/enumerable-lazy
http://shugomaeda.blogspot.in/2012/03/enumerablelazy-and-its-benefits.html
As I already said in my comments, implementing such a thing as lazy arrays wouldn't be sensible.
Using Enumerable instead can work nicely in some situations, but differs from lazy lists in some points: methods like map and filter won't be evaluated lazily (so they won't work on infinite enumerables) and elements that have been calculated once aren't stored, so if you access an element twice, it's calculated twice.
If you want the exact behavior of haskell's lazy lists in ruby, there's a lazylist gem which implements lazy lists.
This will loop to infinity:
0.step{|i| puts i}
This will loop to infinity twice as fast:
0.step(nil, 2){|i| puts i}
This will go to infinity, only if you want it to (results in an Enumerator).
table_of_3 = 0.step(nil, 3)
I surprised no one answered this question appropriately yet
So, recently I found this method Enumerator.produce which in conjunction with .lazy does exactly what you described but in ruby-ish fashion
Examples
Enumerator.produce(0) do
_1 + 2
end.lazy
.map(&:to_r)
.take(1_000)
.inject(&:+)
# => (999000/1)
def fact(n)
= Enumerator.produce(1) do
_1 + 1
end.lazy
.take(n)
.inject(&:*)
fact 6 # => 720
The right answer has already identified the "lazy" method, but the example provided was not too useful. I will give a better example of when it is appropriate to use lazy with arrays. As stated, lazy is defined as an instance method of the module Enumerable, and it works on EITHER objects that implement Enumerable module (e.g. arrays - [].lazy) or enumerators which are the return value of iterators in the enumerable module (e.g. each_slice - [].each_slice(2).lazy). Note that in Enumerable module, some of the instance methods return more primitive values like true or false, some return collections like arrays and some return enumerators. Some return enumerators if a block is not given.
But for our example, the IO class also has an iterator each_line, which returns an enumerator and thus can be used with "lazy". The beautiful thing about returning an enumerator is that it does not actually load the collection (e.g. large array) in memory that it is working on. Rather, it has a pointer to the collection and then stories the algorithm (e.g. each_slice(2)) that it will use on that collection, when you want to process the collection with something like to_a, for example.
So if you are working with an enumerator for a huge performance boost, now you can attach lazy to the enumerator. So instead of iterating through an entire collection to match this condition:
file.each_line.select { |line| line.size == 5 }.first(5)
You can invoke lazy:
file.each_line.lazy.select { |line| line.size == 5 }.first(5)
If we're scanning a large text file for the first 5 matches, then once we find the 5 matches, there is no need to proceed the execution. Hence, the power of lazy with any type of enumerable object.
Ruby Arrays dynamically expand as needed. You can apply blocks to them to return things like even numbers.
array = []
array.size # => 0
array[0] # => nil
array[9999] # => nil
array << 1
array.size # => 1
array << 2 << 3 << 4
array.size # => 4
array = (0..9).to_a
array.select do |e|
e % 2 == 0
end
# => [0,2,4,6,8]
Does this help?

Ruby: How to loop through an object that may or may not be an array?

I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.

Resources