what does [0..1] mean in Module.constants() method? - ruby

In the following code
Module.constants[0..1] # => [:object, :Module]
What does the [0..1] mean here?

0..1 is a range. It's syntactic sugar for the Ruby parser to create a Range object. You can do a lot with ranges, including simple iteration:
irb(main):003:0> (1..3).class
=> Range
irb(main):004:0> (1..3).each {|x| puts x}
1
2
3
=> 1..3
You can turn it into an Array, among other things:
irb(main):005:0> (1..3).to_a
=> [1, 2, 3]
When you use a Range as an Array#[] argument, it means you want all the elements whose index is in that range (inclusive):
irb(main):007:0> stuff = %w{a b c d e f}
=> ["a", "b", "c", "d", "e", "f"]
irb(main):008:0> range = 2..4
=> 2..4
irb(main):009:0> stuff[range]
=> ["c", "d", "e"]

Module.constants returns an array of all the constants defined in (i.e. namespaced to) the Module class (yes, Module is a class; see Module.class). The [0..1] says give me every element of the array from the 0th to the 1st. In general, if x is an array, then x[m..n] returns the subarray of x consisting of the elements from the mth to the nth. For example:
x = [36, 25, 16, 9, 4]
x[1..3] # => [25, 16, 9]

Related

How to add user specified values from a hash

I'm trying to create a program that would take a string from user input and return the 'value' of the word where a=1, b=2, c=3 etc. i.e. "cab" = 6.
Unfortunately I can't figure out how to break down the user input variable and have it added together:
print "Give us a word to calculate: "
word = gets.chomp
alphabet = Hash[
"a" => 1, "b" => 2, "c" => 3,
"d" => 4, "e" => 5, "f" => 6,
"g" => 7, "h" => 8, "i" => 9,
"j" => 10, "k" => 11, "l" => 12,
"m" => 13, "n" => 14, "o" => 15,
"p" => 16, "q" => 17, "r" => 18,
"s" => 19, "t" => 20, "u" => 21,
"v" => 22, "w" => 23, "x" => 24,
"y" => 25, "z" => 26
]
value = word.split("")
puts "Your word, \'#{word}\' has a value of: #{value}"
You can use reduce method to add up the values of each char.
value = word.split("")
sum = value.reduce(0) {|sum, char| alphabet[char] + sum }
puts "Your word, \'#{word}\' has a value of: #{sum}"
#=> Your word, 'cab' has a value of: 6
Here we use reduce (which has an alias method inject) to reduce the array into a single value. We start with initial value of 0, and iterate through each element of the array - in the block, we add the numeric equivalent of given char to the sum so far - and eventually end up with sum of all numeric values.
Answer to question in comments:
My only relevant follow-up question to this, is it possible to define
the hash using ranges? I know that I can define them with ("a".."z")
and (1..26) but I didn't know if there is a way to set those two
ranges equal to one another based on their index values or somesuch
You can make use of Array#zip method that allows to merge two arrays by pairing elements at same index as sub-arrays. Subsequently, we can take advantage of method Array#to_h which converts any array of 2-element arrays into hash.
alphabet = ('a'..'z').zip(1..26).to_h
I'd suggest the following as a good Ruby-way:
base = 'a'.ord-1
"catsup".each_char.map { |c| c.ord - base }.reduce(:+)
#=> 80
Breaking it down:
d = 'a'.ord
#=> 97
base = d-1
#=> 96
e = "catsup".each_char.map { |c| c.ord - base }
#=> [3, 1, 20, 19, 21, 16]
e.reduce(:+)
#=> 80
Let's look more carefully at the calculation of e:
enum0 = "catsup".each_char
#=> #<Enumerator: "catsup":each_char>
Note:
enum0.map { |c| c.ord - base }
#=> [3, 1, 20, 19, 21, 16]
To see the elements of the enumerator enum0, which will be passed to map, convert it to an array:
enum0.to_a
#=> ["c", "a", "t", "s", "u", "p"]
Now lets write:
enum1 = enum0.map
#=> #<Enumerator: #<Enumerator: "catsup":each_char>:map>
Study the return value. You can think of enum1 as a "compound" enumerator.
enum1.to_a
#=> ["c", "a", "t", "s", "u", "p"]
enum1.each { |c| c.ord - base }
#=> [3, 1, 20, 19, 21, 16]
We can now use Enumerator#next to extract each element of enum, set the block variable c to that value and perform the block calculation:
c = enum1.next #=> "c"
c.ord - base #=> 99-96 = 3
c = enum1.next #=> "a"
c.ord - base #=> 1
c = enum1.next #=> "t"
c.ord - base #=> 20
c = enum1.next #=> "s"
c.ord - base #=> 19
c = enum1.next #=> "u"
c.ord - base #=> 21
c = enum1.next #=> "p"
c.ord - base #=> 16
c = enum1.next #=> StopIteration: iteration reached an end

Shuffle array with exceptions

Is there a way to shuffle all elements in an array with the exception of a specified index using the shuffle function?
Without having to manually write a method, does Ruby support anything similar?
For example, say I have an array of integers:
array = [1,2,3,4,5]
and I want to shuffle the elements in any random order but leave the first int in its place. The final result could be something like:
=> [1,4,3,2,5]
Just as long as that first element remains in its place. I've obviously found workarounds by creating my own methods to do this, but I wanted to see if there was some sort of built in function that could help cut down on time and space.
The short answer is no. Based on the latest Ruby documentation of Array.shuffle the only argument it accepts is random number generator. So you will need to write your own method - here's my take on it:
module ArrayExtender
def shuffle_except(index)
clone = self.clone
clone.delete_at(index)
clone.shuffle.insert(index, self[index])
end
end
array = %w(a b c d e f)
array.extend(ArrayExtender)
print array.shuffle_except(1) # => ["e", "b", "f", "a", "d", "c"]
print array.shuffle_except(2) # => ["e", "a", "c", "b", "f", "d"]
There is no built in function. It's still pretty easy to do that:
first element
arr = [1, 2, 3, 4, 5]
hold = arr.shift
# => 1
arr.shuffle.unshift(hold)
# => [1, 4, 5, 2, 3]
specific index
arr = [1, 2, 3, 4, 5]
index = 2
hold = arr.delete_at(index)
# => 3
arr.shuffle.insert(index, hold)
# => [5, 1, 3, 2, 4]

How do I output the index of elements in an array that are also in another?

I have two arrays:
A = ["a","s","p","e","n"]
V = ["a","e","i","o","u"]
I want to output an array that shows the index of every element in array A that is also an element anywhere in V.
In other words:
some_function(A, V) == [0,3]
This is because A[0]="a" and A[3]="e" matches the elements "a" and "e" in array V. How do I do that?
Here is how I would do:
A = ["a","s","p","e","n"]
V = ["a","e","i","o","u"]
A.each_index.select{|i| V.include? A[i]} # => [0, 3]
If V is a Set of data (order doesn't matter, no duplicates), and it is large, then you might get a performance benefit by converting it to a Set so that the include? runs faster since Set is built on a hash and gets O(1) retrieval time:
require 'set'
A = ["a","s","p","e","n"]
V = Set.new ["a","e","i","o","u"]
A.each_index.select{|i| V.include? A[i]} # => [0, 3]
As #Arup has answered your question, I thought I might elaborate a bit. Arup suggested you do this:
A.each_index.select{|i| V.include? A[i]}
where
A = ["a","s","p","e","n"]
V = ["a","e","i","o","u"]
Firstly, what is A.each_index? Try it in IRB:
e = A.each_index # => #<Enumerator: ["a", "s", "p", "e", "n"]:each_index>
e.class # => Enumerator
e.to_a # => [0, 1, 2, 3, 4]
So the enumerator e is the receiver of the method Enumerable#select, Enumerable being a mix-in module that is included by several Ruby classes, including Enumerator. Want to check that?
e.respond_to?(:select) # => true
e.respond_to?(:map) # => true
e.respond_to?(:reduce) # => true
Next, note that A.each_index does not depend on the contents of A, just its size, so we could replace that with any enumerator that iterates from 0 to A.size - 1, such as:
m = A.size
m.times.select{|i| V.include? A[i]} # => [0, 3]
0.upto(m-1).select{|i| V.include? A[i]} # => [0, 3]
We can confirm these are Enumerator objects:
m.times.class # => Enumerator
0.upto(m-1).class # => Enumerator
The other main classes that include Enumerable are Array, Hash, Set, Range and IO (but, since Ruby 1.9, not String), so we could also do this:
Array(0...m).select{|i| V.include? A[i]} # => [0, 3]
(0...m).select{|i| V.include? A[i]} # => [0, 3]
require 'set'
Set.new(0..m-1).select{|i| V.include? A[i]} # => [0, 3]
Note that, regardless of the receiver's class, select returns an array. Most (but not all) Enumerable methods that return a collection, return an array, regardless of the receiver's class.

How to use an enumerator

In the Ruby Array Class documentation, I often find:
If no block is given, an enumerator is returned instead.
Why would I not pass a block to #map? What would be the use of my doing just:
[1,2,3,4].map
instead of doing:
[1,2,3,4].map{ |e| e * 10 } # => [10, 20, 30, 40]
Can someone show me a very practical example of using this enumerator?
Good question.
What if we want to do multiple things with the enumerator that is created? We don't want to process it now, because it means we may need to create another later?
my_enum = %w[now is the time for all good elves to get to work].map # => #<Enumerator: ["now", "is", "the", "time", "for", "all", "good", "elves", "to", "get", "to", "work"]:map>
my_enum.each(&:upcase) # => ["NOW", "IS", "THE", "TIME", "FOR", "ALL", "GOOD", "ELVES", "TO", "GET", "TO", "WORK"]
my_enum.each(&:capitalize) # => ["Now", "Is", "The", "Time", "For", "All", "Good", "Elves", "To", "Get", "To", "Work"]
The main distinction between an Enumerator and most† other data structures in the Ruby core library (Array, Hash) and standard library (Set, SortedSet) is that an Enumerator can be infinite. You cannot have an Array of all even numbers or a stream of zeroes or all prime numbers, but you can definitely have such an Enumerator:
evens = Enumerator.new do |y|
i = -2
y << i += 2 while true
end
evens.take(10)
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
zeroes = [0].cycle
zeroes.take(10)
# => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
So, what can you do with such an Enumerator? Well, three things, basically.
Enumerator mixes in Enumerable. Therefore, you can use all Enumerable methods such as map, inject, all?, any?, none?, select, reject and so forth. Just be aware that an Enumerator may be infinite whereas map returns an Array, so trying to map an infinite Enumerator may create an infinitely large Array and take an infinite amount of time.
There are wrapping methods which somehow "enrich" an Enumerator and return a new Enumerator. For example, Enumerator#with_index adds a "loop counter" to the block and Enumerator#with_object adds a memo object.
You can use an Enumerator just like you would use it in other languages for external iteration by using the Enumerator#next method which will give you either the next value (and move the Enumerator forward) or raise a StopIteration exception if the Enumerator is finite and you have reached the end.
† Eg., an infinite range: (1..1.0/0)
The feature of returning a enumerable when no block is given is mostly used when chaining functions from the enumerable class together. Like this:
abc = %w[a b c]
p abc.map.with_index{|item, index| [item, index]} #=> [["a", 0], ["b", 1], ["c", 2]]
edit:
I think my own understanding of this behavior is a bit too limited in order to give a proper understanding of the inner workings of Ruby. I think the most important thing to note is that arguments are passed on in the same way they are for Procs. Thus if an array is passed in, it will be automatically 'splatted' (any better word for this?). I think the best way to get an understanding is to just use some simple functions returning enumerables and start experimenting.
abc = %w[a b c d]
p abc.each_slice(2) #<Enumerator: ["a", "b", "c", "d"]:each_slice(2)>
p abc.each_slice(2).to_a #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x| x} #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x,y| x+y} #=> ["ab", "cd"]
p abc.each_slice(2).map{|x,| x} #=> ["a", "c"] # rest of arguments discarded because of comma.
p abc.each_slice(2).map.with_index{|array, index| [array, index]} #=> [[["a", "b"], 0], [["c", "d"], 1]]
p abc.each_slice(2).map.with_index{|(x,y), index| [x,y, index]} #=> [["a", "b", 0], ["c", "d", 1]]
In addition to hirolau's answer, there is another method lazy that you can combine to modify the enumerator.
a_very_long_array.map.lazy
If map always took a block, then there would have to be another method like map_lazy, and similarly for other iterators to do the same thing.
I guess sometimes you want to pass the Enumerator to another method, despite where this Enumerator came from: map, slice, whatever:
def report enum
if Enumerator === enum
enum.each { |e| puts "Element: #{e}" }
else
raise "report method requires enumerator as parameter"
end
end
> report %w[one two three].map
# Element: one
# Element: two
# Element: three
> report (1..10).each_slice(2)
# Element: [1, 2]
# Element: [3, 4]
# Element: [5, 6]
# Element: [7, 8]
# Element: [9, 10]
Enumerator A class which allows both internal and external iteration
=> array = [1,2,3,4,5]
=> array.map
=> #<Enumerator: [2, 4, 6, 8, 10]:map>
=> array.map.next
=> 2
=> array.map.next_values
=> [0] 2

Weird multiplicator operator behavior in a two arrays to hash combination

I was looking for a way to convert two arrays into a single hash. I found something like this :
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
Hash[*a1.zip(a2).flatten]
I thought that this syntax was a bit weird, because Hash[a1.zip a2] would do exactly the same. But more than that, I don't understand the need for the * operator.
I know that it turns objects into arrays, or something alike (but not in the same way [] does, apparently).
When I execute :
a = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
a = *a1.zip(a).flatten
=> [1, "A", 2, "B", 3, "C"]
Nothing really happens, and for what I know of the * operator, this seems to be the normal behavior.
So, why does
Hash[*a1.zip(a2).flatten]
=> {1=>"A", 2=>"B", 3=>"C"}
Hash[a1.zip(a).flatten]
=> {}
Return different values, given that the parameters seem identical ?
I guess I must be missing something about the * operator.
Thanks.
When the * operator is used with arrays like that it is called the splat operator.
Think of it as an operator that removes the first level of brackets around an array. This is quite useful because you can turn arrays into argument lists:
def stuff(x, y, z)
end
a = [1, 2, 3]
stuff(*a) # x,y,z gets assigned 1,2,3
The same thing works with Hash[]. The [] operator on Hash accepts as arguments:
An argument list of key-value pairs:
Hash["a", 1, "b", 2] #=> { "a" => 1, "b" => 2 }
An array or array pairs representing key-values:
Hash[ [["a", 1], ["b", 2]] ] #=> { "a" => 1, "b" => 2 }
Hash[] not does NOT accept a plain flat array as arguments:
Hash[ ["a", 1, "b", 2] ] #=> {}
So with this in mind, plus our understanding what the splat operator does you can now see what is happening:
paired_array = a1.zip(a2)
=> [[1, "A"], [2, "B"], [3, "C"]]
plain_array = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
# Per rule 2 above we know this works
Hash[paired_array]
=> {1=>"A", 2=>"B", 3=>"C"}
# This won't work
Hash[plain_array]
=> {}
# But if we turn the plain_array into an argument list,
# then we know per rule 1 above that this will work
Hash[*plain_array]
=> {1=>"A", 2=>"B", 3=>"C"}
Now then you might be wondering what the hey is happening when you do:
a = *plain_array
=> [1, "A", 2, "B", 3, "C"]
Since we know the splat operator effectively strips the brackets, we get this:
a = 1, "A", 2, "B", 3, "C"
...which funnily enough is valid Ruby code and just creates an array again.
You can read more fun stuff about the splat operator in the rubyspec test case for the splat operator.
I think there's a mistake in your example, it should be like this:
Hash[a1.zip(a2).flatten] #=> {}
Hash[*a1.zip(a2).flatten] #=> {1=>"A", 2=>"B", 3=>"C"}
The splat operator in the assign mode converts an array to multiple arguments:
duck, cow, pig = *["quack","mooh","oing"] #=> ["quack","mooh","oing"]
Actually it's identical to
duck, cow, pig = ["quack","mooh","oing"] #=> ["quack","mooh","oing"]
But from the documentation you can see that Hash[...] receives multiple arguments, so the splat operator helps to assign each of those multiple arguments.
It's not that mysterious:
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
p Hash[*a1.zip(a2).flatten] #{1=>"A", 2=>"B", 3=>"C"}
The * converts the array to a mere list (of arguments).
But why wasn't this syntax used?
p Hash[a1.zip(a2)]# {1=>"A", 2=>"B", 3=>"C"}
Well, it is new since Ruby 1.9.2. Your example is probably older.

Resources