Ruby: reverse, mutating list - ruby

I'm trying to write a method, which reverses a list, but not using .reverse.
Here is my code:
def reverse(list)
a = list.length
while a >= 0
list << list[a]
a = a - 1
end
list
end
print reverse([1,2,3])
My expected result isn't [3,2,1] but [1, 2, 3, nil, 3, 2, 1]
Do you have any advice how to not repeat the original list once again, but only mutate it?

This mutates the original array as requested. nil is eliminated by being aware that the last element of the list is at list[list.length-1].
def reverse(list)
a = list.length-1
while a >= 0
list << list[a]
list.delete_at(a)
a = a - 1
end
list
end
p reverse([1, 2, 3]) #=> [3, 2, 1]
A more Ruby way could be as follows:
arr.sort_by!.with_index { |_,i| -i }

I understand the list is to be reversed in place (mutated). Below are two ways to do that.
If the list is not to be mutated, simply operate on a copy:
def non_mutating_reverse(list)
reverse(list.dup)
end
#1
Use parallel assignment (sometimes called multiple assignment).
def reverse(list)
(list.size/2).times { |i| list[i], list[-1-i] = list[-1-i], list[i] }
list
end
list = [1,2,3]
reverse list #=> [3, 2, 1]
list #=> [3, 2, 1]
Notice that when the size of the list is odd (as in this example), the middle element is not moved.
#2
def reverse(list)
list.replace(list.size.times.with_object([]) { |i,a| a.unshift(list[i]) })
end
list = [1,2,3]
reverse list #=> [3, 2, 1]
list #=> [3, 2, 1]

Related

How to improve algorithm efficiency for nested loop

Given a list of integers and a single sum value, return the first two values (from the left) that add up to form the sum.
For example, given:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
[5, 5] (at indices [1, 5] of [10, 5, 2, 3, 7, 5]) add up to 10, and [3, 7] (at indices [3, 4]) add up to 10. Among them, the entire pair [3, 7] is earlier, and therefore is the correct answer.
Here is my code:
def sum_pairs(ints, s)
result = []
i = 0
while i < ints.length - 1
j = i+1
while j < ints.length
result << [ints[i],ints[j]] if ints[i] + ints[j] == s
j += 1
end
i += 1
end
puts result.to_s
result.min
end
It works, but is too inefficient, taking 12000 ms to run. The nested loop is the problem of inefficiency. How could I improve the algorithm?
Have a Set of numbers you have seen, starting empty
Look at each number in the input list
Calculate which number you would need to add to it to make up the sum
See if that number is in the set
If it is, return it, and the current element
If not, add the current element to the set, and continue the loop
When the loop ends, you are certain there is no such pair; the task does not specify, but returning nil is likely the best option
Should go superfast, as there is only a single loop. It also terminates as soon as it finds the first matching pair, so normally you wouldn't even go through every element before you get your answer.
As a style thing, using while in this way is very unRubyish. In implementing the above, I suggest you use ints.each do |int| ... end rather than while.
EDIT: As Cary Swoveland commented, for a weird reason I thought you needed indices, not the values.
require 'set'
def sum_pairs(arr, target)
s = Set.new
arr.each do |v|
return [target-v, v] if s.include?(target-v)
s << v
end
nil
end
sum_pairs [10, 5, 2, 3, 7, 5], 10
#=> [3, 7]
sum_pairs [10, 5, 2, 3, 7, 5], 99
#=> nil
I've used Set methods to speed include? lookups (and, less important, to save only unique values).
Try below, as it is much more readable.
def sum_pairs(ints, s)
ints.each_with_index.map do |ele, i|
if ele < s
rem_arr = ints.from(i + 1)
rem = s - ele
[ele, rem] if rem_arr.include?(rem)
end
end.compact.last
end
One liner (the fastest?)
ary = [10, 0, 8, 5, 2, 7, 3, 5, 5]
sum = 10
def sum_pairs(ary, sum)
ary.map.with_index { |e, i| [e, i] }.combination(2).to_a.keep_if { |a| a.first.first + a.last.first == sum }.map { |e| [e, e.max { |a, b| a.last <=> b.last }.last] }.min { |a, b| a.last <=> b.last }.first.map{ |e| e.first }
end
Yes, it's not really readable, but if you add methods step by step starting from ary.map.with_index { |e, i| [e, i] } it's easy to understand how it works.

Ruby Getting a max value out of a newly created array in one function

I want my function to return the longest Array within a nested array (including the array itself) so
nested_ary = [[1,2],[[1,2,[[1,2,3,4,[5],6,7,11]]]],[1,[2]]
deep_max(nested_ary)
=> [1,2,3,4,[5],6,7,11]
simple_ary = [1,2,3,4,5]
deep_max(simple_ary)
=> returns: [1,2,3,4,5]
I created a function to collect all arrays. I have to get the max value in another function.
my code:
def deep_max(ary)
ary.inject([ary]) { |memo, elem|
if elem.is_a?(Array)
memo.concat(deep_max(elem))
else
memo
end }
end
This gives me what I want:
deep_max(nested_ary).max_by{ |elem| elem.size }
Is there a way to get this max inside of the function?
def deep_max(arr)
biggest_so_far = arr
arr.each do |e|
if e.is_a?(Array)
candidate = deep_max(e)
biggest_so_far = candidate if candidate.size > biggest_so_far.size
end
end
biggest_so_far
end
deep_max [[1, 2], [[1, 2, [[1, 2, 3, 4, [5], 6, 7, 11]]]], [1, [2]]]
#=> [1, 2, 3, 4, [5], 6, 7, 11]
You can unroll it:
def deep_max(ary)
arys = []
ary = [ary]
until ary.empty?
elem = ary.pop
if elem.is_a?(Array)
ary.push(*elem)
arys.push(elem)
end
end
arys.max_by(&:size)
end
Or you can cheat, by introducing an optional parameter that changes how your recursion works on top level vs how it behaves down the rabbit hole.

ruby** reverse array without using reverse method

This is my array and custom method to reverse an array output without using the reverse method. not sure where it broke, tried running it in console, no dice.
numbers = [1, 2, 3, 4, 5, 6]
def reversal(array)
do |item1, item2| item2 <=> item1
end
p reversal(numbers)
Here's one way to handle this. This is not very efficient but works.
def reversal(array)
reversed = []
loop do
reversed << array.pop
break if array.empty?
end
reversed
end
Here is another implementation that does the same thing:
def reversal(array)
array.each_with_index.map do |value, index|
array[array.count-index-1]
end
end
So many ways... Here are three (#1 being my preference).
numbers6 = [1, 2, 3, 4, 5, 6]
numbers5 = [1, 2, 3, 4, 5]
For all methods my_rev below,
my_rev(numbers6)
#=> [6, 5, 4, 3, 2, 1]
my_rev(numbers5)
#=> [5, 4, 3, 2, 1]
#1
def my_rev(numbers)
numbers.reverse_each.to_a
end
#2
def my_rev(numbers)
numbers.each_index.map { |i| numbers[-1-i] }
end
#3
def my_rev(numbers)
(numbers.size/2).times.with_object(numbers.dup) do |i,a|
a[i], a[-1-i] = a[-1-i] , a[i]
end
end
there are so many ways to do this
1 Conventional way
a=[1,2,3,4,5,6,7,8]
i=1
while i <= a.length/2 do
temp = a[i-1]
a[i-1] = a[a.length-i]
a[a.length-i] = temp
i+=1
end
2 Using pop
a=[1,2,3,4,5,6]
i=0
b=[]
t=a.length
while i< t do
b << a.pop
i+=1
end
3 Using pop and loop
a=[1,2,3,4,5,6]
b=[]
loop do
b << a.pop
break if a.empty?
end
a = [1,2,3,4,5]
b = []
a.length.times { |i| b << a[(i+1)*-1] }
b
=> [5,4,3,2,1]

How can I remove duplicates in an array without using `uniq`?

The object of my coding exercise is to get rid of duplicates in an array without using the uniq method. Here is my code:
numbers = [1, 4, 2, 4, 3, 1, 5]
def my_uniq(array)
sorted = array.sort
count = 1
while count <= sorted.length
while true
sorted.delete_if {|i| i = i + count}
count += 1
end
end
return sorted
end
When I run this, I get an infinite loop. What is wrong?
Can I use delete the way that I am doing with count?
How will it execute? Will count continue until the end of the array before the method iterates to the next index?
I did this with each or map, and got the same results. What is the best way to do this using each, delete_if, map, or a while loop (with a second loop that compares against the first one)?
Here is a clearly written example.
numbers = [1, 4, 2, 4, 3, 1, 5]
def remove_duplicates(array)
response = Array.new
array.each do |number|
response << number unless response.include?(number)
end
return response
end
remove_duplicates(numbers)
As others pointed out, your inner loop is infinite. Here's a concise solution with no loops:
numbers.group_by{|n| n}.keys
You can sort it if you want, but this solution doesn't require it.
the problem is that the inner loop is an infinite loop:
while true
sorted.delete_if {|i| i = i + count}
count += 1
end #while
you can probably do what you are doing but it's not eliminating duplicates.
one way to do this would be:
numbers = [1, 4, 2, 4, 3, 1, 5]
target = []
numbers.each {|x| target << x unless target.include?(x) }
puts target.inspect
to add it to the array class:
class ::Array
def my_uniq
target = []
self.each {|x| target << x unless target.include?(x) }
target
end
end
now you can do:
numbers = [1, 4, 2, 4, 3, 1, 5]
numbers.my_uniq
You count use Set that acts like an array with does not allow duplicates:
require 'set'
numbers = [1, 4, 2, 4, 3, 1, 5]
Set.new(numbers).to_a
#=> [1, 4, 2, 3, 5]
Try using Array#& passing the array itself as parameter:
x = [1,2,3,3,3]
x & x #=> [1,2,3]
This is one of the answer. However, I do not know how much of performance issue it takes to return unique
def my_uniq(ints)
i = 0
uniq = []
while i < ints.length
ints.each do |integers|
if integers == i
uniq.push(integers)
end
i += 1
end
end
return uniq
end

How to refactor this code to remove output variable?

def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.

Resources