Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Could someone please explain lines 5 and 6? On line 5, is |word| a parameter? Why is it needed there? Also, on line 6, are {|a, b| b} also parameters. How should one read line 6? What is it doing?
puts "Input something: " # 1
text = gets.chomp # 2
words = text.split # 3
frequencies = Hash.new(0) # 4
words.each { |word| frequencies[word] += 1 } # 5
frequencies = frequencies.sort_by {|a, b| b} # 6
frequencies.reverse! # 7
On line 5, is |word| a parameter?
Yes, it's a block argument.
Why is it needed there?
From Array#each's documentation: "Calls the given block once for each element in self, passing that element as a parameter."
Example:
words = ["foo", "bar", "baz"]
words.each { |word| puts word }
The block is called three times. On the first pass, its block argument word is set to "foo", on the second pass it's set to "bar" and on the third pass it's set to "baz". Each time word is printed using puts.
Output:
foo
bar
baz
In your example, a hash is used to store the word frequencies. Within the each loop, the word's count is incremented.
How should one read line 6? What is it doing?
Enumerable#sort_by sorts a collection by the block's result. For example, to sort an array of strings by the string's length you would use:
["xxx", "xx", "x"].sort_by { |str| str.length }
#=> ["x", "xx", "xxx"]
Since frequencies is a hash, the block is called for each pair. Therefore, two arguments are set - a is the pair's key and b is the pair's value:
frequencies = { "foo" => 3, "bar" => 2, "baz" => 1}
frequencies = frequencies.sort_by { |a, b| b }
#=> [["baz", 1], ["bar", 2], ["foo", 3]]
It sorts the hash by its values. Note that sort_by returns an array. The array is assigned to the frequencies variable.
Instead of a and b you could use more descriptive argument names:
frequencies.sort_by { |word, count| count }
Related
I'm trying to reverse a string without using the built-in reverse method to get something like this:
input: "hello, world"
output: "world hello,"
I've been able to reverse the string to "dlrow ,olleh" so the words are the in the order they should be, but I'm stuck on how to reverse the individual words.
Suppose
str = "Three blind mice"
Here are three ways you could obtain the desired result, "mice blind Three", without using the method Array#reverse.
Split string, add index, use Enumerable#sort_by to sort array by index, join words
str.split.each_with_index.sort_by { |_,i| -i }.map(&:first).join(' ')
The steps are as follows.
a = str.split
#=> ["Three", "blind", "mice"]
enum = a.each_with_index
#=> #<Enumerator: ["Three", "blind", "mice"]:each_with_index>
b = enum.sort_by { |_,i| -i }
#=> [["mice", 2], ["blind", 1], ["Three", 0]]
c = b.map(&:first)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
We can see the elements that will be generated by enum and passed to sort_by by converting enum to an array:
enum.to_a
#=> [["Three", 0], ["blind", 1], ["mice", 2]]
A disadvantage of this method is that it sorts an array, which is a relatively expensive operation. The next two approaches do not share that weakness.
Split string, use Array#values_at to extract words by index, highest to lowest join words
arr = str.split
arr.values_at(*(arr.size-1).downto(0).to_a).join(' ')
The steps are as follows.
arr = str.split
#=> ["Three", "blind", "mice"]
a = arr.size-1
#=> 2
b = a.downto(0).to_a
#=> [2, 1, 0]
c = arr.values_at(*b)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
Use String#gsub to create an enumerator, chain to Enumerator#with_object, build string
str.gsub(/\w+/).with_object('') { |word,s|
s.prepend(s.empty? ? word : word + ' ') }
The steps are as follows.
enum1 = str.gsub(/\w+/)
#=> #<Enumerator: "Three blind mice":gsub(/\w+/)>
enum2 = enum1.with_object('')
#=> #<Enumerator: #<Enumerator: "Three blind mice":
# gsub(/\w+/)>:with_object("")>
enum2.each { |word,s| s.prepend(s.empty? ? word : word + ' ') }
#=> "mice blind Three"
When String#gsub is called on str without a block it returns an enumerator (see doc). The enumerator generates, and passes to with_object, matches of its argument, /\w+/; that is, words. At this point gsub no longer performs character replacement. When called without a block it is convenient to think of gsub as being named each_match. We can see the values that enum1 generates by converting it to an array (or execute Enumerable#entries on enum1):
enum1.to_a
#=> ["Three", "blind", "mice"]
Though Ruby has no such concept, it may be helpful to think of enum2 as a compound enumerator (study the return value for enum2 = enum1.with_object('') above). It will generate the following values, which is will pass to Enumerator#each:
enum2.to_a
#=> [["Three", ""], ["blind", ""], ["mice", ""]]
The second value of each of these elements is the initial value of the string that will be built and returned by each.
Let's now look at the first element element being generated by enum2 and passed to the block:
word, s = enum2.next
#=> ["Three", ""]
This first step is called Array decomposition.
word
#=> "Three"
s #=> ""
The block calculation is then as follows.
s.empty?
#=> true
t = word
#=> "Three"
s.prepend(t)
#=> "Three"
s #=> "Three"
Now the second element is generated by enum2 and passed to the block
word, s = enum2.next
#=> ["blind", "Three"]
word
#=> "blind"
s #=> "Three"
s.empty?
#=> false
t = word + ' '
#=> "blind "
s.prepend(t)
#=> "blind Three"
Notice that the value of second element of the array returned by enum2.next, the current value of s, has been updated to "Three".
The processing of the third and final element generated by enum2 (["mice", "blind Three"]) is similar, resulting in the block returning the value of s, "mice blind Three".
so I posted a question earlier about displaying factors of non-prime numbers and got this as a solution.
Below is a part of the code, but I have a little bit trouble understanding a few terms in them (since I am relatively new to ruby).
def factors(n)
(1..n/2).select{|e| (n%e).zero?}.push(n)
end
For instance,
What does the method select do in general?
What does .zero? do?
Then,
puts "#{n} is not a prime number =>#{factors(n).join(',')}"
What does .join(',') do?
It would be greatly appreciated if anyone can explain these terms to me in basic terms or simple concepts.
select method filters a collection. You specify the condition (the predicate) in the block and select returns items who match that condition.
[1,2,3,4].select(&:even?)
=> [2, 4]
i.zero? is another way to write i == 0
[0,2,3,0].select(&:zero?)
=> [0, 0]
join method concatenate a collection as a string with the parameter as a separators between items
[0,2,3,0].select(&:zero?).join(' and ')
=> "0 and 0"
Note
[1,2,3].select(&:even?)
is a simpler way to write
[1,2,3].select { |item| item.even? }
i will try explain by following links and text from documentation
join()
Returns a string created by converting each element of the array to a string, separated by the given separator. If the separator is nil, it uses current $,. If both the separator and $, are nil, it uses empty string.
[ "a", "b", "c" ].join #=> "abc"
[ "a", "b", "c" ].join("-") #=> "a-b-c"
select
Returns a new array containing all elements of ary for which the given block returns a true value.
If no block is given, an Enumerator is returned instead.
[1,2,3,4,5].select { |num| num.even? } #=> [2, 4]
a = %w{ a b c d e f }
a.select { |v| v =~ /[aeiou]/ } #=> ["a", "e"]
zero?
zero? → true or false
Returns true if num has a zero value.
Ruby html documentation: ruby-doc.org
Have the following code which should select every other character of a string and make a new string out of them:
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.join
end
However, select returns this output with test case "hello":
"h0l2o4"
When using map instead I get the desired result:
"hlo"
Is there a reason why select would not work in this case? In what scenarios would it be better to use map over select and vice versa
If you still want to use select, try this.
irb(main):005:0> "hello".chars.select.with_index {|m, index| m if index % 2 == 0}.join
=> "hlo"
each_with_index does not work because it is selecting both the character and the index and then joining all of that.
The reason that select does not work in this case is that select "Returns an array containing all elements of enum for which the given block returns a true value" (see the doc here), so what you get in your case is an array of arrays [['h',0],['l',2],['o',4]] which you then join to get "h0l2o4".
So select returns a subset of an enumerable. map returns a one to one mapping of the provided enumerable. For example the following would "fix" your problem by using map to extract character from each value returned by select.
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.map { |pair| pair.first }.join
end
puts(bits "hello")
=> hlo
For lots of reasons this is not a good way to get every other character from a string however.
Here is another example using map. In this case each index is mapped to either the character or nil then joined.
def bits(string)
string.chars.each_index.map {|i| string[i] if i.even? }.join
end
If you use Enumerable#map, you will return an array having one element for each character in the string.
arr = "try this".each_char.map.with_index { |c,i| i.even? ? c : nil }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
which is the same as
arr = "try this".each_char.map.with_index { |c,i| c if i.even? }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
My initial answer suggested using Array#compact to remove the nils before joining:
arr.compact.join
#=> "tyti"
but as #npn notes, compact is not necessary because Array#join applies NilClass.to_s to the nil's, converting them to empty strings. Ergo, you may simply write
arr.join
#=> "tyti"
Another way you could use map is to first apply Enumerable#each_cons to pass pairs of characters and then return the first character of each pair:
"try this".each_char.each_cons(2).map(&:first).join
#=> "tyti"
Even so, Array#select is preferable, as it returns only the characters of interest:
"try this".each_char.select.with_index { |c,i| i.even? }.join
#=> "tyti"
A variant of this is:
even = [true, false].cycle
#=> #<Enumerator: [true, false]:cycle>
"try this".each_char.select { |c| even.next }.join
#=> "tyti"
which uses Array#cycle to create the enumerator and Enumerator#next to generate its elements.
One small thing: String#each_char is more memory-efficient than String#chars, as the former returns an enumerator whereas the latter creates a temporary array.
In general, when the receiver is an array,
use map when you want to return an array containing one element for each element of the receiver.
use Enumerable#find when you want to return just one element of the receiver.
use Array#select or Array#reject (or Enumerable#select or Enumerable#reject if the receiver is an enumerator).
Me, I'd use a simple regular expression:
"Now is the time to have fun.".scan(/(.)./).join
#=> "Nwi h iet aefn"
How would I write a case statement that would list all elements in an array, allow the user to pick one, and do processing on that element?
I have an array:
array = [ 'a', 'b', 'c', 'd' ]
Ultimately I'd like it to behave like this:
Choices:
1) a
2) b
3) c
4) d
Choice =>
After the user picks 3, I would then do processing based off the choice of the user. I can do it in bash pretty easily.
Ruby has no built-in menu stuff like shell scripting languages do. When doing menus, I favor constructing a hash of possible options and operating on that:
def array_to_menu_hash arr
Hash[arr.each_with_index.map { |e, i| [i+1, e] }]
end
def print_menu menu_hash
puts 'Choices:'
menu_hash.each { |k,v| puts "#{k}) #{v}" }
puts
end
def get_user_menu_choice menu_hash
print 'Choice => '
number = STDIN.gets.strip.to_i
menu_hash.fetch(number, nil)
end
def show_menu menu_hash
print_menu menu_hash
get_user_menu_choice menu_hash
end
def user_menu_choice choice_array
until choice = show_menu(array_to_menu_hash(choice_array)); end
choice
end
array = %w{a b c d}
choice = user_menu_choice(array)
puts "User choice was #{choice}"
The magic happens in array_to_menu_hash:
The [] method of Hash converts an array with the form [ [1, 2], [3, 4] ] to a hash {1 => 2, 3 => 4}. To get this array, we first call each_with_index on the original menu choice array. This returns an Enumerator that emits [element, index_number] when iterated. There are two problems with this Enumerator: the first is that Hash[] needs an array, not an Enumerator. The second is that the arrays emitted by the Enumerator have the elements in the wrong order (we need [index_number, element]). Both of these problems are solved with #map. This converts the Enumerator from each_with_index into an array of arrays, and the block given to it allows us to alter the result. In this case, we are adding one to the zero-based index and reversing the order of the sub-arrays.
I have an array arr. I want to destructively remove elements from arr based on a condition, returning the removed elements.
arr = [1,2,3]
arr.some_method{|a| a > 1} #=> [2, 3]
arr #=> [1]
My first try was reject!:
arr = [1,2,3]
arr.reject!{|a| a > 1}
but the returning blocks and arr's value are both [1].
I could write a custom function, but I think there is an explicit method for this. What would that be?
Update after the question was answered:
partition method turns out to be useful for implementing this behavior for hash as well. How can I remove elements of a hash, returning the removed elements and the modified hash?
hash = {:x => 1, :y => 2, :z => 3}
comp_hash, hash = hash.partition{|k,v| v > 1}.map{|a| Hash[a]}
comp_hash #=> {:y=>2, :z=>3}
hash #=> {:x=>1}
I'd use partition here. It doesn't modify self inplace, but returns two new arrays. By assigning the second array to arr again, it gets the results you want:
comp_arr, arr = arr.partition { |a| a > 1 }
See the documentation of partition.
All methods with a trailing bang ! modify the receiver and it seems to be a convention that these methods return the resulting object because the non-bang do so.
What you can to do though is something like this:
b = (arr.dup - arr.reject!{|a| a>1 })
b # => [2,3]
arr #=> [1]
Here is a link to a ruby styleguide which has a section on nameing - although its rather short
To remove (in place) elements of array returning the removed elements one could use delete method, as per Array class documentation:
a = [ "a", "b", "b", "b", "c" ]
a.delete("b") #=> "b"
a #=> ["a", "c"]
a.delete("z") #=> nil
a.delete("z") { "not found" } #=> "not found"
It accepts block so custom behavior could be added, as needed