Ruby: undefined method 'to_set' for array? - ruby

I have a large array of chars:
input = ["p", "f", "p", "t" ... "g"]
I am attempting to take a slice of the array and convert it into a set:
sub = input.slice(0, 4).to_set
But the interpreter bombs:
undefined method `to_set' for ["p", "f", "p", "t"]:Array (NoMethodError)
Why is this happening? In irb this code executes with no issues.

The Enumerable#to_set method is implemented by Ruby's Set. It is not require-d by default hence why you get the error if you try to use it.
But in irb Set is already required. You can verify that by:
require 'set' # => false
This is something that has been raised up as an issue in irb before.

Related

what does map! do in ruby

I'm fairly new to ruby and I'm a bit confused by what the map! syntax does. I see the following line on the codebase
b.map(&:values).uniq!.map!
b.map(&:values).uniq! gives me the following output:
[["us"],
["au"],
["fr"],
["mx"],
["ad",
"ae",
"af",
"al",
"am",
"ao",
"aq"]]
When I add a .map! to the end of b.map(&:values).uniq! I get #<Enumerator: ...>. I'm not really sure what is happening. If anyone could explain to me what is going on that would be very helpful.
From the documentation:
Invokes the given block once for each element of self, replacing the element with the value returned by the block.
See also Enumerable#collect.
If no block is given, an Enumerator is returned instead.
This means that if you have used map! with a block - the array would have replaced all its elements with the return value of the block:
b.map(&:values).uniq!.map!(&:first)
# => ["us", "au", "fr", "mx", "ad"]
Since you did not add a block, you got an Enumerator, which is like a delayed action cursor, where you can add your block later on:
enum = b.map(&:values).uniq!.map!
enum.each(&:first)
# => ["us", "au", "fr", "mx", "ad"]

Couldn't understand the difference between `puts{}.class` and `puts({}.class)`

As the anonymous block and hash block looks like approximately same. I was doing kind of playing with it. And doing do I reached to some serious observations as below:
{}.class
#=> Hash
Okay,It's cool. empty block is considered as Hash.
print{}.class
#=> NilClass
puts {}.class
#=> NilClass
Now why the above code showing the same as NilClass,but the below code shows the Hash again ?
puts ({}.class)
#Hash
#=> nil
print({}.class)
#Hash=> nil
Could anyone help me here to understand that what's going one above?
I completely disagree with the point of #Lindydancer
How would you explain the below lines:
print {}.class
#NilClass
print [].class
#Array=> nil
print (1..2).class
#Range=> nil
Why not the same with the below print [].class and print (1..2).class?
EDIT
When ambiguity happens with local variable and method call, Ruby throws an error about the fact as below :
name
#NameError: undefined local variable or method `name' for main:Object
# from (irb):1
# from C:/Ruby193/bin/irb:12:in `<main>'
Now not the same happens with {} (as there is also an ambiguity between empty code block or Hash block). As IRB also here not sure if it's a empty block or Hash. Then why the error didn't throw up when IRB encountered print {}.class or {}.class?
The precedence rules of ruby makes print{}.class interpreted as (print{}).class. As print apparently returns a nil the class method returns #NilClass.
EDIT: As been discussed on other answers and in the updates to the question, print{} it of course interpreted as calling print with a block, not a hash. However, this is still about precedence as {} binds stronger than [] and (1..2) (and stronger than do ... end for that matter).
{} in this case is recognized as block passed to print, while [] unambiguously means empty array.
print {}.class # => NilClass
print do;end.class # => NilClass
You are running into some nuances of Ruby, where characters mean different things depending on context. How the source code is interpreted follows rules, one of which is that {} is a closure block if it follows a method call, and otherwise a Hash constructor.
It's common throughout the language to see characters mean different things depending on context or position within the statement.
Examples:
Parens () used for method call or for precedence
print(1..5).class => NilClass
print (1..5).class => Range <returns nil>
Square brackets [] used to call :[] method or for Array
print[].class => NoMethodError: undefined method `[]' for nil:NilClass
print([].class) => Array <returns nil>
Asterisk * used for multiplication or splatting
1 * 5 => 5
[*1..5] => [1, 2, 3, 4, 5]
Ampersand & used for symbol -> proc or logical and
0 & 1 => 0
[1, 2, 3].map(&:to_s) => ["1", "2", "3"]
Or in your case, braces used for block closures or for a hash
... hope it makes sense now ...

Frozen objects in Ruby

In Ruby, what does it mean for a String or Array (etc) object to be 'Frozen'? How/where is this property set or modified?
It means you cannot modify it. You set it by freeze method.
s = "a"
concat modifies the string instance.
s.concat("b")
# => "ab"
When you freeze the string:
s.freeze
then, you cannot apply concat any more.
s.concat("c")
# => RuntimeError: can't modify frozen String
However, you can apply methods that do not modify the receiver:
s + "c"
# => "abc"
Prevents further modifications to obj. A RuntimeError will be raised if modification is attempted. There is no way to unfreeze a frozen object. See also Object#frozen?.
a = [ "a", "b", "c" ]
a.freeze
a << "z"
produces:
prog.rb:3:in `<<': can't modify frozen array (RuntimeError)
from prog.rb:3
Doco

Is this expected behaviour for a Set of arrays in Ruby?

We're doing a bit of work in Ruby 1.8.7 that requires traversing and partitioning an undirected graph, that has been failing weirdly in production. When I distil the failing code down to its barest components, I get this strangely failing test:
it 'should be able to clear a ruby set of arrays' do
a = ["2", "b", "d"]
b = ["1", "a", "c", "e", "f"]
set = Set.new([a, b])
a.concat(b)
p "before clear: #{set.inspect}"
set.clear
p "after clear: #{set.inspect}"
set.size.should == 0
end
The test fails with this output:
"before clear: #<Set: {[\"1\", \"a\", \"c\", \"e\", \"f\"], [\"2\", \"b\", \"d\", \"1\", \"a\", \"c\", \"e\", \"f\"]}>"
"after clear: #<Set: {[\"2\", \"b\", \"d\", \"1\", \"a\", \"c\", \"e\", \"f\"]}>"
expected: 0
got: 1 (using ==)
Attempts to delete from the set also behave in strange ways. I'm guessing that Ruby is getting hung up on the hash values of the keys in the array changing under concat(), but surely I should still be able to clear the Set. Right?
There is a workaround for this, if you duplicate the set after you modify the keys, the new set will have the updated keys and clear properly. So setting set = set.dup will fix that problem.
The .dup approach was indeed my first work-around, and did as advertised.
I ended up adding the following monkey-patch to Set:
class Set
def rehash
#hash.rehash
end
end
which allows me to rehash the set's keys after any operation that changes their hash values.
This appears to all be fixed in Ruby 1.9.

Reading multiple lines at time from a file

How can I read multiple lines at a time from a file in Ruby?
I tried using each_slice(2) and also did: require 'enumerator', but it doesn't work. I get the following error:
undefined method `each_slice' for #<String:0x877d12c> (NoMethodError)
Both IO and String have a lines enumerator, which you can call each_slice on:
irb(main):004:0> STDIN.lines.each_slice(2).take(2)
a
a
b
c
=> [["a\n", "a\n"], ["b\n", "c\n"]]
Of course you can substitute STDIN with any other IO instance (open file). Demo with a string:
irb(main):005:0> "a\na\nb\nc".lines.each_slice(2).to_a
=> [["a\n", "a\n"], ["b\n", "c"]]
Both of these work in Ruby >= 1.8.7

Resources