Sorting a Ruby array - ruby

I am trying to sort large inputs in the fastest way in ascending order. The code is something like this:
t=gets
ti=t.to_i
r=[]
for i in(0..ti)
k=gets
r[i]=k.to_i
end
r.sort_by{|x| -x.last}
This is giving me an error saying undefined method 'last' for nil:nilclass <nomethoderror>
from tsort.rb: in sort_by
from tsort.rb in 'each'
from tsort.rb in 'sort_by'
I don't know where am I wrong.
That's what I have tried for sorting an array...which is r[] which has all the numbers in t! Can anyone please help.
My inputs are less than 10^6!

I can't reproduce your exact error, I get undefined method 'last' for n:Fixnum (NoMethodError). That makes sense, because you're calling the last method on x, which will hold the values of your Array r, all Fixnums, which do not have a last method.
It should work if you replace the last line with:
r.sort
The sort method will sort your Array in ascending order by default.

If you want to sort a list of integers taken from STDIN I suggest something like the following:
lines = STDIN.readlines.map { |x| x.strip.to_i }.sort
puts lines.join(', ')
It's cleaner, more rubyish and faster (read the documentation for Enumerable.sort_by to see why sort is a better alternative to sort_by).
I also see your code expects a number that says how many lines to read. You can get the same behavior by modifying the example above as follows:
line_count = gets.strip.to_i
lines = (1..line_count).collect { gets.strip.to_i }.sort
puts lines.join(', ')

Try the sort function on Array's think it does what you need.

Related

Iterating Over Multidimensional Arrays in Ruby

I'm going over iteration within multidimensional arrays in Ruby on Codecademy and came across a question I can't seem to find the answer to. So, in their example, they show that a multidimensional array can be iterated using the following code:
things = [[1,2,3], ["red", "blue"]]
things.each do |sub_array|
sub_array.each do |item|
puts item
end
end
This prints out the values of both sub_arrays. However, if I only want to display one sub_array, how would I go about that? I have tried the following code but I'm getting an undefined method `each' for 2:Fixnum error.
things = [[1,2,3], ["red", "blue"]]
things.each do |numbers, colors|
colors.each { |item| puts item }
end
So, I guess my question is why my code is not functioning correctly and how I would go about printing out only the array at index 1?
Your block parameters deconstruct the array as follows:
The enumerator generated by :each yields each element of the outer array in sequence, and then applies the pattern matching based on the structure of the block parameters. So in the first iteration, you have [1,2,3] yielded to the block, which is then mapped to numbers = 1 and colors = 2. 3 is ignored because it doesn't fit the pattern.
If you only want to display one sub-array, you don't need iterate over the whole array- just grab the required element by the index (if you know what the index is):
things[1].each {|color| ... }
Or, you can assign it to a variable in a similar way. As long as you know the colors will always be in the second position, you can do this:
_, colors = *things
colors.each {|color| ... }

Concatenating multiple arrays in ruby

I'm trying to make a method that accepts a nested array. From that nested array, I need to return all possible combinations that can be made by the sub-arrays. I have been working on it for hours now and I still can't get it to work.
It's like concatenating each element of a sub_array with each element from the other sub_arrays.
example:
mega_array = [["a","b"],["c","d"],["e","f"]]
my_method(mega_array)
=> ["ace","acf","ade","adf","bce","bcf","bde","bdf"]
This would have been accomplished by the following code:
mega_array[0].each do |first|
mega_array[1].each do |second|
mega_array[2].each do |third|
puts first + second + third
end
end
end
Unfortunately, the number of subarrays can vary. This is where I'm stuck. Tried to do some recursive techniques but I still don't get it right.
Help will be greatly appreciated. I need to make this work with the vanilla Ruby that comes with Leopard and up. It's 1.8.7, right?
Thanks.
>> mega_array[0].product(*mega_array[1..-1]).map(&:join)
=> ["ace", "acf", "ade", "adf", "bce", "bcf", "bde", "bdf"]

What is an efficient way to read a line of integers from the STDIN in ruby?

I have tried
STDIN.gets.chomp.split(" ")
And then converted each elements in the array to Integer.
But I believe there should be a better solution.
Assuming the integers cannot be negative:
nums = gets.scan(/\d+/).map(&:to_i)
Or for arbitrary input:
nums = gets.strip.split(/\s+/).map(&:to_i)
The key here is using using Array#map to invoke a block for each value in the array, creating a new array of the results for each. Then we use Symbol#to_proc (invoked by the ampersand syntax of Ruby) to tersely invoke the to_i method on each string.
For example, foo.map(&:to_i) is equivalent to foo.map{ |o| o.to_i }.
Edit: it will be slightly faster to map the array in place, and split only on a single space if you know that is the only separator, and to not use Symbol#to_proc:
nums = gets.chomp.split(' ').map!{ |o| o.to_i }
However, you are unlikely to see more than a very minor improvement here. Are you certain that you need this faster? What is your data, and what are your profiling results showing that this is a critical place where you need more speed?

help with a simple method

Hi I'm trying to solve the exercise from https://github.com/alexch/learn_ruby
I must write a method which should "multiplies two numbers" and "multiplies an array of numbers". I'm fresh to ruby and solved it, with only one method like this:
def multi(*l)
sum = 1
l.flatten! if l.is_a? Array
l.each{|i| sum = sum*i}
return sum
end
I'm sure there are better ways, so how can I improve this method? to a more ruby like syntax :)
The if l.is_a? Array is not necessary because the way multi is define, l will always be an array.
The pattern
result = starting_value
xs.each {|x| result = result op x}
result
can be written more succinctly using xs.inject(starting_value, :op).
So you can write your code as:
def multi(*l)
l.flatten.inject(1, :*)
end
If you're ok, with calling the method as multi(*array) instead of multi(array) to multiply an array, you can leave out the flatten, as well.

incorrect use of ruby's sort_by function

My sort function is clearly not working, I am trying to sort ALL_VIEWS by #permalink.length:
ALL_VIEWS.sort_by {|view| view.permalink.length}
ALL_VIEWS[0..4].each do |view|
puts view.inspect
puts view.permalink.length
end
this produces:
#<View:0x1014da7b8 #permalink="xxxx">
4
#<View:0x1014da790 #permalink="yyyyy">
5
#<View:0x1014da718 #permalink="zzz">
3
#<View:0x1014da6a0 #permalink="aaaaaaa">
7
#<View:0x1014da628 #permalink="b">
1
I am expecting the following result:
#<View:0x1014da628 #permalink="b">
1
#<View:0x1014da718 #permalink="zzz">
3
#<View:0x1014da7b8 #permalink="xxxx">
4
#<View:0x1014da790 #permalink="yyyyy">
5
#<View:0x1014da6a0 #permalink="aaaaaaa">
7
Use
ALL_VIEWS.replace ALL_VIEWS.sort_by {|view| view.permalink.length }
for the first line. sort_by does not sort in place - it returns the sorted array and does not modify the original one.
The Problem
sort_by does not mutate the object, it is a method of Enumerable that returns an Array whose elements are the elements yielded by your object's each method. It then sorts that array by the criteria given in your block. But notice that it does not sort your array docs for Enumerable#sort_by
You could normally do what Adrian suggested, and assign the results back to the original variable, but your original is a constant, which is going to give you issues.
The Solution(s)
So you need to either make ALL_VIEWS something other than a constant (if you are wanting to change it, then it really shouldn't be a constant anyway). Perhaps a class variable?
Or you need to have these values sorted before they are assigned to ALL_VIEWS
ALL_VIEWS = view_paths.map do |path|
View.new 'views/pages' , path
end.sort_by do |view|
view.permalink.length
end
Or you can sort in place with something other than the sort_by method
ALL_VIEWS.sort! do |view1,view2|
view1.permalink.length <=> view2.permalink.length
end
This will work, but again, if you are doing stuff like this, then should ALL_VIEWS really be a constant? docs for Array#sort and Array#sort!

Resources