Ruby: insert and sort numbers - arrays

I have to create a method in Ruby which inserts a number and sorts the resulting list.
Input would be like:
insert_number([2.0,3.5,4.8], 4.1)
which should output:
[2.0,3.5,4.1,4.8]
With input like:
insert_number([], 5.1)
it should output:
[5.1]
Here is my incomplete code:
def insert_number(list, number)
new_list = []
position = 0
number_has_been_inserted = false # Remember whether a new number
# has been inserted.
while position < list.length
position += 1
new_list = list + [number]
...
end
...
new_list
end
print insert_number([2.0,3.5,4.8], 4.1)

bsearch only works if the original input array is already sorted, which is not a pre-condition. – #pjs
Considering your original array is sorted you can use binary search here. It will perform much better because it won't need to perform expensive sorting procedure on each insert.
This one mutates original array
def insert_number(arr, num)
i = (0...arr.size).bsearch{ |a| arr[a] > num }
i ||= arr.size
arr.insert(i, num)
end
arr = []
insert_number(arr, 1)
#=> [1]
insert_number(arr, 2)
# => [1, 2]
insert_number(arr, 2.1)
# => [1, 2, 2.1]
insert_number(arr, 1.3)
#=> [1, 1.3, 2, 2.1]
And this one will return new array on each call
def insert_number(arr, num)
i = (0...arr.size).bsearch{ |a| arr[a] > num }
i ||= arr.size
arr[0, i] + [num] + arr[i..-1]
# or
# arr.dup.insert(i, num)
end
arr = []
arr = insert_number(arr, 1)
#=> [1]
arr = insert_number(arr, 2)
# => [1, 2]
arr = insert_number(arr, 2.1)
# => [1, 2, 2.1]
arr = insert_number(arr, 1.3)
#=> [1, 1.3, 2, 2.1]
PS:
Recent Ruby versions have bsearch_index – #Stefan

I'd do something like:
def insert_number(list, number)
(list << number).sort
end
list = [1, 2, 3]
insert_number(list, 2.1) # => [1, 2, 2.1, 3]
insert_number(list, 4.1) # => [1, 2, 2.1, 3, 4.1]
insert_number([], 1) # => [1]
The problem is this changes list. If that's not desired then use dup:
def insert_number(list, number)
(list.dup << number).sort
end
list = [1, 2, 3]
insert_number(list, 2.1) # => [1, 2, 2.1, 3]
insert_number(list, 4.1) # => [1, 2, 3, 4.1]
insert_number([], 1) # => [1]
or a "splat" AKA *:
def insert_number(list, number)
[*list, number].sort
end
list = [1, 2, 3]
insert_number(list, 2.1) # => [1, 2, 2.1, 3]
insert_number(list, 4.1) # => [1, 2, 3, 4.1]
insert_number([], 1) # => [1]
[*list, number] tells Ruby to explode the array list into its elements, effectively creating a new array:
[1, 2, 3, number]

A little shorter without having to use dup.
def insert_number(list, number)
(list + [number]).sort
end

use Array#sort:
def insert_number(list,number=false)
number.is_a?(Numeric) ? list.push(number).sort : list.sort
end
The above method uses the ternary operator which is a short form of an if-statement.

Use In-Place Array Methods
In-place operations on the array should be fastest, since they won't have to create additional arrays. For example:
#array = [2.0, 3.5, 4.8]
def insert_number float
(#array << float).sort!
end
If you can't operate directly on a shared variable, or want to reduce coupling, then you can still shave some time with the in-place sort rather than returning a new sorted array:
def insert_number array, float
(array.dup << float).sort!
end
In all cases, appending to an array with << should be faster than creating new arrays with the + method.

Actually we do not need any sort because we know that our array is sorted, and then when we will new number x we can put it into array with O(n) time, where n is size of array. We will go one by one, let's take i as index and our new array will be built like NewArray = (left numbers < x) + x + (x < right number): here left numbers are that numbers less than x and right number are numbers bigger than x.
ispushed = false
arr.each do |number|
if number < x
NewArray.push(number)
elsif ispushed == false
ispushed=true
NewArray.push(x)
newArray.push(number)
else
NewArray.push(number)
if our x is biggest so that ispushed will be remained false
in the end we can just push our 'x' number
if you use sort it works by O(nlogn) time complexity

Related

If there's two maximum elements of an array?

In this code if user type 2, two times and 1, two times. Then there's two maximum elements and both Kinder and Twix should be printed. But how ? I probably can do this with if method but this will make my code even longer. Any cool version? Can I do this with just one if?
a = [0, 0, 0,]
b = ["Kinder", "Twix", "Mars"]
while true
input = gets.chomp.to_i
if input == 1
a[0] += 1
elsif input == 2
a[1] += 1
elsif input == 3
a[2] += 1
elsif input == 0
break
end
end
index = a.index(a.max)
chocolate = b[index] if index
print a.max,chocolate
The question really has nothing to do with how the array a is constructed.
def select_all_max(a, b)
mx = a.max
b.values_at(*a.each_index.select { |i| a[i] == mx })
end
b = ["Kinder", "Twix", "Mars"]
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
See Array#values_at.
This could alternatively be done in a single pass.
def select_all_max(a, b)
b.values_at(
*(1..a.size-1).each_with_object([0]) do |i,arr|
case a[i] <=> arr.last
when 0
arr << i
when 1
arr = [i]
end
end
)
end
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
p select_all_max [1, 1, 1], b
["Kinder", "Twix", "Mars"]
One way would be as follows:
First, just separate the input-gathering from the counting, so we'll just gather input in this step:
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
Now we can tally up the inputs. If you have Ruby 2.7 you can simply do counts_by_input = inputs.tally to get { "Twix" => 2, "Kinder" => 2 }. Otherwise, my preferred approach is to use group_by with transform_values:
counts_by_input = inputs.group_by(&:itself).transform_values(&:count)
# => { "Twix" => 2, "Kinder" => 2 }
Now, since we're going to be extracting values based on their count, we want to have the counts as keys. Normally we might invert the hash, but that won't work in this case because it will only give us one value per key, and we need multiple:
inputs_by_count = counts_by_input.invert
# => { 2 => "Kinder" }
# This doesn't work, it removed one of the values
Instead, we can use another group_by and transform_values (the reason I like these methods is because they're very versatile ...):
inputs_by_count = counts_by_input.
group_by { |input, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
# => { 2 => ["Twix", "Kinder"] }
The transform_values code here is probably a bit confusing, but one important thing to understand is that often times, calling Enumerable methods on hashes converts them to [[key1, val1], [key2, val2]] arrays:
counts_by_input.group_by { |input, count| count }
# => { 2 => [["Twix", 2], ["Kinder", 2]] }
Which is why we call transform_values { |keyvals| keyvals.map(&:first) } afterwards to get our desired format { 2 => ["Twix", "Kinder"] }
Anyway, at this point getting our result is very easy:
inputs_by_count[inputs_by_count.keys.max]
# => ["Twix", "Kinder"]
I know this probably all seems a little insane, but when you get familiar with Enumerable methods you will be able to do this kind of data transformation pretty fluently.
Tl;dr, give me the codez
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
inputs_by_count = inputs.
group_by(&:itself).
transform_values(&:count).
group_by { |keyvals, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
top_count = inputs_by_count.keys.max
inputs_by_count[top_count]
# => ["Twix", "Kinder"]
How about something like this:
maximum = a.max # => 2
top_selling_bars = a.map.with_index { |e, i| b[i] if e == maximum }.compact # => ['Kinder', 'Twix']
p top_selling_bars # => ['Kinder', 'Twix']
If you have
a = [2, 2, 0,]
b = ['Kinder', 'Twix', 'Mars']
You can calculate the maximum value in a via:
max = a.max #=> 2
and find all elements corresponding to that value via:
b.select.with_index { |_, i| a[i] == max }
#=> ["Kinder", "Twix"]

In Ruby, how do you square a numbers in an array? [duplicate]

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 6 years ago.
Improve this question
This is code I currently have
def square_array(array)
array.each do |i|
i ** 2
end
end
I know it's not correct, could someone explain this process to me please?
Using each and << with an empty array
def square_array(array)
arr = []
array.each { |i| arr << i ** 2 }
arr
end
my_arr = [1, 2]
p square_array(my_arr) #=> [1, 4]
Here we've created a new empty array arr. We then iterate through the other array array which is passed as an argument, squaring each element before pushing it (using <<) into our new array arr.
Finally we return the newly created array arr by simply writing arr as the final line in the method block. We could have written return arr but in Ruby the return keyword can be omitted.
Using each_with_object
A slight evolution of the above technique
def square_array(array)
array.each_with_object([]) { |i,arr| arr << i ** 2 }
end
my_arr = [1, 2]
p square_array(my_arr) #=> [1, 4]
Using each with an Enumerator
def square_array(array)
Enumerator.new do |y|
array.each { |e| y << e ** 2 }
end
.take(array.length)
end
my_arr = [1, 2, 3, 4]
p square_array(my_arr) #=> [1, 4, 9, 16]
Here we create a new enumerator. We then write instructions for the enumerator telling it (when called upon) to yield values y according to the each block.
We then call all the yielded values for the given array by using take which returns an array with said values.
You need the index of the array element in order to set it to something else, so we will use each_with_index, and set the original array element to the new value:
irb(main):001:0> j = [2,3,4]
=> [2, 3, 4]
irb(main):002:0> j.each_with_index { |e, i| j[i] = e**2 }
=> [4, 9, 16]
irb(main):003:0>
If you can't use map, it seems natural to use reduce instead:
def square_array(array)
array.reduce([]) { |a, n| a << n * n }
end
But if that violates the spirit of the restriction, you could do it in a more manual way:
def square_array(array)
[].tap do |a|
array.each do { |n| a << n * n }
end
end
You can try this way using simple inject. For Doc refer: INJECT
> ar = [2,3,4,5]
=> [2, 3, 4, 5]
> ar.inject([]){|a,b| a << b**2}
=> [4, 9, 16, 25]
As has been pointed out in comments, map or collect is the natural way to do it in Ruby. However, if you just have to do without:
def square_array(ary)
a = Array.new(ary.length)
ary.each_index { |i| a[i] = ary[i] * ary[i] }
a
end

How to do take! in Ruby?

What is a good way to take (keep) the first n elements in an array and delete the rest?
If there is no built-in method, then
def take! ary, n
...
end
z = (1..10).to_a
take! z, 5
# z is [1,2,3,4,5]
There are several possibilities.
The Swiss Army Knife that always works is Array#replace which simply replaces the contents of the receiver with the contents of the argument, and thus can be used to transform any array into any other array, so you can just say something like:
class Array
def take!(n)
replace(take(n))
end
end
Using Array#slice! is another possibility:
class Array
def take!(n)
slice!(0, n)
end
end
I'd just use Array#slice!
x = [1, 2, 3, 4]
x.slice!(2..-1) # Will take! the first 2 elements of the array
x # => [1, 2]
Try:
def taker(arr, n)
arr.pop(arr.length - n)
arr
end
p taker([1, 2, 3, 4, 5], 2) #=> [1, 2]
Using Array#replace suggests itself:
z = (1..10).to_a
z.object_id # 23576880
def z.take! n
replace(take n)
end
z.take! 5 # [1, 2, 3, 4, 5]
z.object_id # 23576880
Example defines singleton method, but you may define #take! on your array-derived class, using refinement on a module etc.

Destructive reject from an array returning the values rejected

Is there a sensible way to do the following:
I want to take an array and select specific items from the array according to conditions, removing them from the array as they go.
(I basically want to split the contents of an array into categories).
array = [1,2,3,4,5,6,7,8]
less_than_three = array.reject_destructively{|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = array.reject_destructively{|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
I've tried delete_if, select!, reject! and none of them seem to be able to give you the affected items whilst leaving the array with the rest.
Unless I'm going mad, which is entirely possible.
As I understood the question, you do not want to produce two new objects. Here you go:
class Array
def carve!
dup.tap { delete_if &Proc.new } - self
end
end
array = [1,2,3,4,5,6,7,8]
p array.carve! { |v| v < 3 }
#⇒ [1, 2] # returned by Array#carve method
p array
#⇒ [3, 4, 5, 6, 7, 8] # remained in original array
Using this solution, array.__id__ remains the same. And this is the golfiest answer all around :)
You can build your own method for this...
class Array
def extract(&block)
temp = self.select(&block)
self.reject!(&block)
temp
end
end
then...
a = [1, 2, 3, 4, 5]
a.extract{|x| x < 3}
=> [1,2]
p a
=> [3, 4, 5]
EDIT: If you don't want to monkey patch (but monkey patching isn't evil in itself) you can do it with a vanilla method...
def select_from_array(array, &block)
temp = array.select(&block)
array.reject!(&block)
temp
end
array = [1,2,3,4,5,6,7,8]
less_than_three = select_from_array(array){|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = select_from_array(array){|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
In rails 6 there is a method extract!:
a = [1, 2, 3] #=> [1, 2, 3]
a.extract! { |num| num.odd? } #=> [1, 3]
a #=> [2]
irb(main):001:0> array = [1,2,3,4,5,6,7,8]
=> [1, 2, 3, 4, 5, 6, 7, 8]
irb(main):002:0> array.partition{|v| v < 3}
=> [[1, 2], [3, 4, 5, 6, 7, 8]]
is there a specific reason, why this has to be destructive ?
Will this help
class Array
def reject_destructively(&block)
arr = self.select(&block)
arr.each{ |i| self.delete(i) }
arr
end
end
array = [1,2,3,4,5,6,7,8]
p less_than_three = array.reject_destructively{|v| v<3}
#=> [1,2]
p array
#=> [3,4,5,6,7,8]
p more_than_five = array.reject_destructively{|v| v>5}
#=> [6,7,8]
p array
#=> [3,4,5]
The above code can be simplified further to look like:
class Array
def reject_destructively(&block)
self.select(&block).each{ |i| self.delete(i) }
end
end
Ok. This works, avoids monkey patching, keeps it to one line...etc, but it's damn ugly....
less_than_three = array.dup - array.reject!{|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = array.dup - array.reject!{|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
module Enumerable
def reject_destructively
array=[]
self.each do |y|
if yield(y)
array<<y
end
end
array.each do |x|
self.delete(x)
end
return array
end
end
array=[10,9,2,1,3,45,52]
print less_than_three = array.reject_destructively{|v| v < 3}
print array
You can use group_by to get all of the elements that satisfy the condition in one group, and all of the rest in the other.
For example
[1,2,3,4,5].group_by{|i| i > 3}
gives
{false=>[1, 2, 3], true=>[4, 5]}
More information is available at http://ruby-doc.org/core-2.1.1/Enumerable.html#method-i-group_by

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