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

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

Related

Ruby: reverse, mutating list

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]

idiomatic way to check if array contains ordered (but possibly non-continuous) set of elements

I was wondering if there is a more idiomatic way to get the functionality represented by the code below. Basically I just want to check if the array contains the elements in pattern in the order specified by pattern. It's okay for there to be gaps between these elements.
class Array
def has_pattern?(pattern)
offset = 0
pattern.each do |p|
offset = self[offset..-1].index(p)
return false if offset.nil?
end
return true
end
end
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 4, 5]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([2, 3, 1]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 3, 2]) # false
The code above seems to work, but doesn't feel like idiomatic Ruby to me. Is there a nicer way to write this?
Here's my take on it:
class Array
def has_pattern?(ptn)
i = 0
self.each do |elem|
i += 1 if elem == ptn[i]
end
i >= ptn.size
end
end
It passes through the array only once, so it may make a difference when the array's big.
Here's a different way to approach it:
class Array
def has_pattern?(pattern)
(self - (self - pattern))
.each_cons(pattern.length)
.any? { |p| p === pattern }
end
end
But, as I said in the comments above, I think your solution is superior.

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.

Ruby: Multiply all elements of an array

Let's say I have an array A = [1, 2, 3, 4, 5]
how can I multiply all elements with ruby and get the result? 1*2*3*4*5 = 120
and what if there is an element 0 ? How can I ignore this element?
This is the textbook case for inject (also called reduce)
[1, 2, 3, 4, 5].inject(:*)
As suggested below, to avoid a zero,
[1, 2, 3, 4, 5].reject(&:zero?).inject(:*)
There is also another way to calculate this factorial!
Should you want to, you can define whatever your last number is as n.
In this case, n=5.
From there, it would go something like this:
(1..num).inject(:*)
This will give you 120. Also, .reduce() works the same way.
Well, this is a dummy way but it works :)
A = [1, 2, 3, 4, 5]
result = 1
A.each do |i|
if i!= 0
result = result*i
else
result
end
end
puts result
If you want to understand your code later on, use this: Assume A = 5, I used n instead of A
n = 5
n.times {|x| unless x == 0; n = n * x; ++x; end}
p n
To carry it forward, you would:
A = [1,2,3,4,5]
arb = A.first
a = A.count
a.times {|x| arb = arb * A[x]; ++x}
p arb

Randomizing array elements

I have an array #number = [1,2,3,4,5,6,7,8,9]
Now, I want to randomize the array content... something like eg: [5,3,2,6,7,1,8]
Please guide me how to proceed with it.
Use the shuffle method ...
irb(main):001:0> [1,2,3,4,5].shuffle
=> [3, 4, 2, 5, 1]
the shuffle command returns a randomized version of an array
eg:
[1,2,3].shuffle => [2,3,1]
[1,2,3,4,5,6,7,8,9].sort_by {rand}[0,9]
=> [5, 7, 3, 8, 9, 4, 2, 1, 6]
If you are using old version of ruby... this will work
def randomize(array)
b = []
array.length.downto(1) { |n|
b.push array.delete_at(rand(n))
}
b
end
a = [1,2,3,4,5]
b=randomize(a)
print b
loop n times
i = random array index
j = random array index
swap elements i and j
end

Resources