arr.delete() vs arr.delete_at() in Ruby - arrays

I wrote the function below which accepts an array and returns a randomized version of it.
I've noticed that I sometimes end up with a nil element in randomizedArr when using list.delete(element) to remove an element from the array, but this does not happen when using list.delete_at(index) -- note that the latter is commented out in the below snippet. Am I missing something?
If there's a better way to do what I'm trying to achieve with this function then I would appreciate any suggestion. Thanks!
The array I'm passing to this function is a string array with ~2k elements. I'm passing in a clone of the original array so it doesn't become empty when the function is called. I'm using Ruby 2.1 on Windows 7.
def getRandomList(list)
randomizedArr = Array.new()
cnt = list.length
while (cnt >= 1) do
index = rand(cnt)
prod = list[index]
randomizedArr.push(prod)
list.delete(prod)
#list.delete_at(index)
cnt = cnt - 1
end
if randomizedArr.include?(nil)
puts "found nil element"
end
return randomizedArr
end #getRandomList()

I am not sure why you need to put all that logic when you can randomize the list by list.shuffle.

Refering to the Ruby documentation this is what I found to answer your question...
#To delete an element at a particular index:
arr = [2, 3, 4, 5] #I added this bit
arr.delete_at(2) #=> 4
arr #=> [2, 3, 5]
#To delete a particular element anywhere in an array, use delete:
arr = [1, 2, 2, 3]
arr.delete(2) #=> 2
arr #=> [1,3]
All of that can be found here https://ruby-doc.org/core-2.4.1/Array.html
arr.delete(2) will remove any instance of 2 in an array while delete_at(2) only removes the third value in the array.

Related

In Ruby, why does plugging in a variable assigned to an array index return undefined?

I am learning Ruby and just solved this pyramid problem. For whatever reason, I tried to change twoD[0] to the variable twoDidx (see third line).
However, when I try replacing while twoD[0].length != 1 with while twoDidx.length != 1, I get "undefined." What am I not understanding about how variables work? Thanks.
def pyramid_sum(base)
twoD = [base]
twoDidx = twoD[0]
while twoD[0].length != 1
arr = twoD[0].map.with_index do |num, idx|
if idx != twoD[0].length - 1
num + twoD[0][idx + 1]
end
end
arr = arr.compact
twoD.unshift(arr)
end
return twoD
end
print pyramid_sum([1, 4, 6]) #=> [[15], [5, 10], [1, 4, 6]]
There's a big difference between twoDidx and twoD[0]. twoDidx is a reference to a first element of twoD at the time you made an assignment while twoD[0] is the reference to the first element of twoD array at the time of execution.
To make it more obvious:
array = [1]
first = array[0] # Here you just assign 1 to the variable
array = [100]
first #=> 1
array[0] #=> 100

How to square an array of numbers in ruby with while without `each` `map` or `collect` methods?

I'm new to coding in RUBY. I'm trying to write a method that squares each element in an array of numbers and returns a new array of these numbers squared. Trying to use while loop and NOT use each, collect, or map. Having trouble understanding how to index/loop each individual element of array and square is (**).
This is what makes sense to me but I know its wrong.
def square_array(numbers)
count = 0
while count < numbers.length do
numbers.index ** 2
end
square_array(numbers)
end
Will anyone please help me? Thanks!
The easy way to do it is map, of course:
def square_array(numbers)
numbers.map { |e| e ** 2 }
end
But here's what you have to do to do the same with a while loop (which is good practice).
Create an array to contain the transformed data.
Create a counter (you've done that).
Set up your while loop (as you have it, except you don't need the do at the end).
Write a statement that squares the array element whose index is the same as your counter, and pushes that result into the array you created in step 1.
Increment your counter by 1 (you forgot to do that, so you'll be getting an endless loop since count will always equal zero).
Return the array you created in step 1.
That will do it for you! See if you can put that together, rather than me just giving you the code.
def square_array(numbers)
# Allocate an array with the same size as `numbers`
# so that the runtime does not have to resize it from time to time
result = Array.new(numbers.size)
# The index
i = 0
while i < numbers.size
# Fill the result array
result[i] = numbers[i] ** 2
# and don't forget to increase the index,
# otherwise the loop will run forever.
i += 1
end
# Return the result array
result
end
The more functional approach would be to use recursion.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
And for any mapper (e. g. square root,) use it like:
fun.([1,2,3,4,5], ->(e) { e ** 2 }, fun)
#⇒ [1, 4, 9, 16, 25]
Please note! This approach mutates the initial array, so it should be explicitly array.dup’ed before passing to the function. To eliminate the necessity to pass the function itself through and leave the initial array intact, we would need a wrapper.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
mapper = ->(arr, map) { fun.([], arr.dup, map, fun) }
And use it like:
arr = [1,2,3,4,5]
mapper.(arr, ->(e) { e ** 2 })
#⇒ [1, 4, 9, 16, 25]
arr
#⇒ [1, 2, 3, 4, 5]
def sq(arr)
enum = arr.each
a = []
loop do
n = enum.next
a << n*n
end
a
end
sq [1, 2, 3, 4]
#=> [1, 4, 9, 16]
See Array#each, Kernel#loop and Enumerator#next. One could use Kernel#to_enum (documented in Object) in place of Array#each.
Using a for loop?
ary = [1,2,3]
res = []
for n in ary do
res << n ** 2
end
res
#=> [1, 4, 9]
But better you stick with map.
Here is my solution:
def square_array(numbers)
new_array = []
counter = 0
while counter < numbers.length()
new_array.push(numbers[counter] * numbers[counter])
counter += 1
end
return new_array
end
Without using each, map, or collect.
def square_array(array)
new_array = []
array.length.times do |index|
new_array.push(array[index] ** 2)
end
new_array
end

Using memoization for storing values in ruby array

For a short array the following function works well. It's supposed to return the first array pair that whe sum is equal to a given integer. However, if the array has a length upwards of 10 million elements, the request times out, because (I think) is storing thousands of values in the variable I create in the first line. I know I have to use memoization (||=) but have no idea how to use it.
array1 = [1,2,3,4,5,6,7]
number = 3
array2 = [1,2,3.....n] # millions of elements
combos = array1.combination(2).to_a
(combos.select { |x,y| x + y == number }).sort.first
I need to gather all possible pairs to sort them, I'm using select to go through the entire list and not stop at the first pair that returns true.
This is one of the possible solutions.
def sum_pairs(ints, s)
seen = {}
for i in ints do
return [s-i, i] if seen[s-i]
seen[i] = true
end
nil
end
def find_smallest(arr, nbr)
first, *rest = arr.sort
until rest.empty?
matching = rest.bsearch { |n| n == nbr - first }
return [first, matching] unless matching.nil?
first, *rest = rest
end
nil
end
arr = [12, 7, 4, 5, 14, 9]
find_smallest(arr, 19) #=> [5, 14]
find_smallest(arr, 20) #=> nil
I've used the method Array#bsearch (rather than Enumerable#find to speed up the search for an element equal to nbr - first (O(log rest.size) vs. O(rest.size)).

Find the index by current sort order of an array in ruby

If there is an array
array A = ["a","b","c","d"] #Index is [0,1,2,3]
And it's sorted to.
array A = ["d","c","b","a"]
I need an array that returns me the updated index based on the sorted order
[3,2,1,0]
I'm trying to find a solution to this ruby
UPDATE to the question
If a is sorted to
array A = ["d","b","c","a"] #not a pure reverse
Then the returned index array should be
[3,1,2,0]
You need to create a mapping table that preserves the original order, then use that order to un-map the re-ordered version:
orig = %w[ a b c d ]
orig_order = orig.each_with_index.to_h
revised = %w[ d c b a ]
revised.map { |e| orig_order[e] }
# => [3, 2, 1, 0]
So long as your elements are unique this will be able to track any shift in order.
Here is one way to do this:
original_array = ["a","b","c","d"]
jumbled_array = original_array.shuffle
jumbled_array.map {|i| original_array.index(i)}
#=> [1, 3, 0, 2]
Note:
In this sample, output will change for every run as we are using shuffle to demonstrate the solution.
The solution will work only as long as array has no duplicate values.
If you do wish to solution to work with arrays with duplicate values, then, one possibility is to look at object_id of array members while figuring out the index.
jumbled_array.map {|i| original_array.map(&:object_id).index(i.object_id)}
This solution will work as long as jumbled_array contains element from original_array and no elements were recreated using dup or something that results in change in object_id values
You can use the map and index methods.
arr = ["a","b","c","d"]
sort_arr = ["d","c","b","a"]
sort_arr.map{|s| arr.index(s)}
# => [3, 2, 1, 0]

How to find duplicates in array without using `uniq` method

I am doing a challenge to make a method that finds duplicate values in an array, and prints out a new array without the duplicates. Ruby has a built in uniq method; however, I am not allowed to use it.
In my mind, this should work:
def uniques(array)
tempPos = 0
arrayPos = 0
duplicate = true
result = [] # array the result will be "pushed" too
for arrayPos in 0..array.length
for tempPos in 0..array.length
# If the values at the indexes are the same. But the indexes are not the same.
# we have a duplicate
if array[arrayPos] == array[tempPos] && arrayPos != tempPos
duplicate = true
else
duplicate = false
end
if duplicate == false
result[arrayPos] = array[arrayPos]
end
end
puts duplicate
end
puts result.inspect
end
Output:
uniq *this is the short hand user input to run the method*
false
false
false
false
false
false
[1, 2, 1, 4, 5, nil]
I must be doing something wrong.
Are you allowed to use a Set?
require 'set'
array = [1, 2, 3, 3, 3, 4]
Set.new(array).to_a
#=> [1, 2, 3, 4]
An other way is to iterate over every pair in the array:
array.each_cons(2).with_object([array.first]) do |pair, result|
result << pair.last unless pair.first == pair.last
end
#=> [1, 2, 3, 4]
There are many ways to do that. Here's another. Suppose:
arr = [3,5,1,3,4,1,1]
Construct:
h = arr.group_by(&:itself)
#=> {3=>[3, 3], 5=>[5], 1=>[1, 1, 1], 4=>[4]}
The duplicates are given by:
h.select { |_,v| v.size > 1 }.keys
#=> [3, 1]
and an array without the duplicates is given by:
h.keys
#=> [3, 5, 1, 4]
Your logic works fine altough as mentioned above a set would work better. You could also sort the elements, and then find adjacent pairs that are the same value which wouldn't work as well as a set, but would have slightly better run-time than your current solution:
To polish what you currently have:
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = false
for tempPos in 0...result.length
# if the values at the indexes are the same... but the indexes are not the same...
# we have a duplicate
duplicate ||= (array[arrayPos] == result[tempPos])
end
if !duplicate
result << array[arrayPos]
end
end
puts result
end
an slightly better approach (altought still poor performance):
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = result.include?(array[arrayPos])
if !duplicate
result << array[arrayPos]
end
end
puts result
end
Although this solution is OK for a learning assignment, you should note that the complexity of this is O(n^2) (n-squared). What that means is that for an array of size n (for example n=10), you are doing n-squared (100) iterations.
It gets exponentially worse. If you have an array of length 1,000,000, you are doing 1,000,000,000,000 iterations. This is why using a set is so important, it's average run-time will be much lower.
A fairly simple way to so this is to leverage array.include?
new = []
arr.each { |x| new << x unless new.include?(x)}
puts new
That will give you an array (new) that only includes unique elements from the original array (arr)
Duplicate array easy way
arr1 = [1,3,4,5,6,6,6,1]
arry = Array.new(arr1)
puts arry
Find uniq array easy way using OR operator
arr1 = [1,3,4,5,6,6,6,1]
arr2 = Array.new # creating new array
arry = arr1 | arr2 # compare two array using OR operator
puts arry

Resources