How to do take! in Ruby? - arrays

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.

Related

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 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

Sort an array of arrays by the number of same occurencies in Ruby

This question is different from this one.
I have an array of arrays of AR items looking something like:
[[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
I would like to sort it by number of same occurences of the second array:
[[7,8,9], [1,2,3], [4,5,6]]
My real data are more complexes, looking something like:
raw_data = {}
raw_data[:grapers] = []
suggested_data = {}
suggested_data[:grapers] = []
varietals = []
similar_vintage.varietals.each do |varietal|
# sub_array
varietals << Graper.new(:name => varietal.grape.name, :grape_id => varietal.grape_id, :percent => varietal.percent)
end
raw_data[:grapers] << varietals
So, I want to sort raw_data[:grapers] by the max occurrencies of each varietals array comparing this value: grape_id inside them.
When I need to sort a classical array of data by max occurencies I do that:
grapers_with_frequency = raw_data[:grapers].inject(Hash.new(0)) { |h,v| h[v] += 1; h }
suggested_data[:grapers] << raw_data[:grapers].max_by { |v| grapers_with_frequency[v] }
This code doesn't work cos there are sub arrays there, including AR models that I need to analyze.
Possible solution:
array.group_by(&:itself) # grouping
.sort_by {|k, v| -v.size } # sorting
.map(&:first) # optional step, depends on your real data
#=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
I recommend you take a look at the Ruby documentation for the sort_by method. It allows you to sort an array using anything associated with the elements, rather than the values of the elements.
my_array.sort_by { |elem| -my_array.count(elem) }.uniq
=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
This example sorts by the count of each element in the original array. This is preceded with a minus so that the elements with the highest count are first. The uniq is to only have one instance of each element in the final result.
You can include anything you like in the sort_by block.
As Ilya has pointed out, having my_array.count(elem) in each iteration will be costlier than using group_by beforehand. This may or may not be an issue for you.
arr = [[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
arr.each_with_object(Hash.new(0)) { |a,h| h[a] += 1 }.
sort_by(&:last).
reverse.
map(&:first)
#=> [[7.8.9]. [1,2,3], [4,5,6]]
This uses the form of Hash::new that takes an argument (here 0) that is the hash's default value.

How do I use an array of values as an array/hash path?

I.e., how do I come from this:
path = [ 1, 3, 4, 5 ... ]
to this:
my_array[1][3][4][5]...
The length of the path array is unknown.
You can use the new (Ruby 2.3) dig method like so:
my_array.dig(1, 3, 4, 5)
Or pass it your splatted array:
path = [1, 3, 4, 5]
my_array.dig(*path)
You can also use inject combined with []:
irb(main):001:0> arr = [[[[0, 1]]]]
=> [[[[0, 1]]]]
irb(main):002:0> [0, 0, 0, 1].inject(arr, :[])
=> 1
This recursively 'unpacks' arr with [] until inject runs out of path elements.
This doesn't require a specific version (inject has been part of Enumerable since Ruby 1.8 at least) but might require a comment explaining what's going on.
Definitely not the way to go, but just for fun:
def build_path(array, path)
eval "#{array}#{path.map { |el| "[#{el}]" }.join}"
end
build_path([1,2], [1])
#=> 2
Is it autovivication you mean ? See http://t-a-w.blogspot.be/2006/07/autovivification-in-ruby.html
You can do that like this
def hash_tree
Hash.new do |hash, key|
hash[key] = hash_tree
end
end
myhash = hash_tree
myhash[1][2][3][4] = 5
myhash # {1=>{2=>{3=>{4=>5}}}}
myhash[1][2][3][4] # 5
or if you prefer the example from the blog
def autovivifying_hash
Hash.new {|ht,k| ht[k] = autovivifying_hash}
end

Ruby: insert and sort numbers

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

Resources