Ruby: How to find the index of the minimum array element? - ruby

Is there any way to rewrite this more elegant? I think, that it's a bad piece of code and should be refactored.
>> a = [2, 4, 10, 1, 13]
=> [2, 4, 10, 1, 13]
>> index_of_minimal_value_in_array = a.index(a.min)
=> 3

I believe this will traverse the array only once and is still easy to read:
numbers = [20, 30, 40, 50, 10] # => [20, 30, 40, 50, 10]
elem, idx = numbers.each_with_index.min # => [10, 4]

This traverses the array only once whereas ary.index(ary.min) would traverse it twice:
ary.each_with_index.inject(0){ |minidx, (v,i)| v < a[minidx] ? i : minidx }

It would be interesting to read about other situations (finding all and only last minimal element).
ary = [1, 2, 1]
# find all matching elements' indexes
ary.each.with_index.find_all{ |a,i| a == ary.min }.map{ |a,b| b } # => [0, 2]
ary.each.with_index.map{ |a, i| (a == ary.min) ? i : nil }.compact # => [0, 2]
# find last matching element's index
ary.rindex(ary.min) # => 2

I actually like #andersonvom 's answer, it only need to loop the array once and still get the index.
And in case you don't want to use ary.each_with_index.min, here is what you can do:
ary = [2,3,4,5,1] # => [2,3,4,5,1]
_, index_of_minimal_value_in_array = ary.each_with_index.min # => [1, 4]
index_of_minimal_value_in_array # => 4

Related

adding array elements using recursion

I need to create a recursive function that adds the numbers of any given array, then removes the first element, then adds the array and do this until the array only has element left.
my function does this but at the moment I can only see the addition by putting a puts statement but also need the results as the return value like this =>[20,20,19,16,10] and dont know how to go about this as its putting the results separately. Thanks for your help.
The function needs to do this recursively:
I this is my code:
def parts_sums(ls)
results_arr=[]
if( ls.length === 1)
return ls
end
p results =ls.sum
parts_sums(ls.drop(1))
p results_arr << results
end
parts_sums([0, 1, 3, 6, 10])
# ls = [0, 1, 3, 6, 10].sum =>20
# ls = [1, 3, 6, 10].sum => 20
# ls = [3, 6, 10].sum =>19
# ls = [6, 10].sum =>16
# ls = [10]=>10
You can define a method like this, which is verbose but clear (I think):
def parts_sums(ary, res = [])
res << [ary.dup, ary.sum]
if ary.size > 1
ary.shift
parts_sums(ary, res)
else
return res
end
end
So, when you call on your array, you get this result:
ary = [0, 1, 3, 6, 10]
parts_sums(ary)
#=> [[[0, 1, 3, 6, 10], 20], [[1, 3, 6, 10], 20], [[3, 6, 10], 19], [[6, 10], 16], [[10], 10]]
Call parts_sums(ary.dup) if you want to preserve the original array.
Which can be rewritten in this shortest way:
def parts_sums_2(ary, res = [])
return res unless ary.any?
res << [ary, ary.sum]
parts_sums_2(ary[1..-1], res)
end
def parts_sums(ls)
ls.length == 1 ? ls : ([ls.sum] + parts_sums(ls[1..-1]))
end
puts parts_sums([0, 1, 3, 6, 10]).to_s

Get items from Ruby Array that occur 2 or more times

Let's say I have a Ruby array.
[1,2,3,4,4,5,6,6,7,7]
I want to find the values that occur 2 or more times.
[4,6,7]
It will help my process to first determine which items occur only once then remove those. So I'd like to solve this by first finding the items that occur once.
There are probably better ways, but this is one:
> [1,2,3,4,4,5,6,6,7,7].group_by{|i| i}.reject{|k,v| v.size == 1}.keys
=> [4, 6, 7]
Breaking it down:
> a = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
> a1 = a.group_by{|i| i}
=> {1=>[1], 2=>[2], 3=>[3], 4=>[4, 4], 5=>[5], 6=>[6, 6], 7=>[7, 7]}
> a2 = a1.reject{|k,v| v.size == 1}
=> {4=>[4, 4], 6=>[6, 6], 7=>[7, 7]}
> a2.keys
=> [4, 6, 7]
Everyone loves a really difficult to follow one liner :)
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h| h[o] += 1 }.select { |_, v| v > 1 }.keys
Add some white space and some comments
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h|
h[o] += 1
}.select { |_, v|
v > 1
}.keys
Enumerate and pass in our memo hash to each iteration the Hash defaults to having 0 for any key
Increment counter for the object
Select only key value pairs where the value is greater than 1
Grab just the keys
This looks quite similar to Phillip's neat answer - in theory this should use slightly less memory as it will not have to build the intermediate arrays to perform counting
Another way:
a = [1,2,3,4,4,5,6,6,7,7]
au = a.uniq
a.reject { |i| au.delete(i) }
#=> [4, 6, 7]
If efficiency is important, you could use a set:
require 'set'
s = Set.new
a.reject { |e| s.add?(e) }
#=> [4, 6, 7]
You can use Array#select to return the elements where Array#count is greater than 1:
2.1.2 :005 > arr = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
2.1.2 :006 > arr.select { |e| arr.count(e) > 1 }.uniq
=> [4, 6, 7]
Hope this helps

How do I filter and defilter an array?

I have an array:
arr = [1,1,2,3,5,8,13,21,34]
I'd like to filter the array in the same way as select but also separately gather all the elements that fail the condition:
[evens, odds] = arr.split_filter {|p| p % 2 == 0}
# evens = [2, 8, 34]
# odds = [1, 1, 3, 5, 13, 21]
I could do
evens = arr.select {|p| p % 2 == 0}
odds = arr.select {|p| p % 2 != 0}
But that seems inefficient. Does anyone know of a function that works like split_filter?
You're looking for Enumerable#partition:
arr = [1,1,2,3,5,8,13,21,34]
evens, odds = arr.partition{|a| a % 2 == 0}
evens # => [2, 8, 34]
odds # => [1, 1, 3, 5, 13, 21]
Or, shorter version:
evens, odds = arr.partition(&:even?)
We could always use Enum#group_by for the same.
arr = [20,1,1,2,3,5,8,13,21,34]
even,odd = arr.group_by(&:even?).values_at(true,false)
even #=> [20, 2, 8, 34]
odd #=> [1, 1, 3, 5, 13, 21]

Is there a particular function to retrieve then delete random array element?

I know I can do this in a couple of steps, but was wondering if there is a function which can achieve this.
I want to array#sample, then remove the element which was retrieved.
How about this:
array.delete_at(rand(array.length))
Another inefficient one, but super obvious what's going on:
array.shuffle.pop
What would be nice would be a destructive version of the sample method on Array itself, something like:
class Array
def sample!
delete_at rand length
end
end
Linuxios's has it perfect. Here is another example:
array = %w[A B C]
item_deleted = array.delete_at(1)
Here it is in irb:
1.9.2p0 :043 > array = %w[A B C]
=> ["A", "B", "C"]
1.9.2p0 :044 > item_deleted = array.delete_at(1)
=> "B"
1.9.2p0 :045 > array
=> ["A", "C"]
1.9.2p0 :047 > item_deleted
=> "B"
An alternative to the rand(array.length) approach already mentioned, could be this one
element = array.delete array.sample
Eksample:
>> array = (1..10).to_a
>> element = array.delete array.sample
>> array # => [1, 2, 4, 5, 6, 7, 8, 9, 10]
>> element # => 3
This is also a set of two operations, but at least you won't have to move away from the array itself.
If you need to sample a number of items and the remove those from the original array:
array = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grab = array.sample(4)
=> [2, 6, 10, 5]
grab.each{ |a| array.delete a }
=> [2, 6, 10, 5]
array
=> [1, 3, 4, 7, 8, 9]

How to return a part of an array in Ruby?

With a list in Python I can return a part of it using the following code:
foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
half = len(foo) / 2
foobar = foo[:half] + bar[half:]
Since Ruby does everything in arrays I wonder if there is something similar to that.
Yes, Ruby has very similar array-slicing syntax to Python. Here is the ri documentation for the array index method:
--------------------------------------------------------------- Array#[]
array[index] -> obj or nil
array[start, length] -> an_array or nil
array[range] -> an_array or nil
array.slice(index) -> obj or nil
array.slice(start, length) -> an_array or nil
array.slice(range) -> an_array or nil
------------------------------------------------------------------------
Element Reference---Returns the element at index, or returns a
subarray starting at start and continuing for length elements, or
returns a subarray specified by range. Negative indices count
backward from the end of the array (-1 is the last element).
Returns nil if the index (or starting index) are out of range.
a = [ "a", "b", "c", "d", "e" ]
a[2] + a[0] + a[1] #=> "cab"
a[6] #=> nil
a[1, 2] #=> [ "b", "c" ]
a[1..3] #=> [ "b", "c", "d" ]
a[4..7] #=> [ "e" ]
a[6..10] #=> nil
a[-3, 3] #=> [ "c", "d", "e" ]
# special cases
a[5] #=> nil
a[6, 1] #=> nil
a[5, 1] #=> []
a[5..10] #=> []
If you want to split/cut the array on an index i,
arr = arr.drop(i)
> arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
> arr.drop(2)
=> [3, 4, 5]
You can use slice() for this:
>> foo = [1,2,3,4,5,6]
=> [1, 2, 3, 4, 5, 6]
>> bar = [10,20,30,40,50,60]
=> [10, 20, 30, 40, 50, 60]
>> half = foo.length / 2
=> 3
>> foobar = foo.slice(0, half) + bar.slice(half, foo.length)
=> [1, 2, 3, 40, 50, 60]
By the way, to the best of my knowledge, Python "lists" are just efficiently implemented dynamically growing arrays. Insertion at the beginning is in O(n), insertion at the end is amortized O(1), random access is O(1).
Ruby 2.6 Beginless/Endless Ranges
(..1)
# or
(...1)
(1..)
# or
(1...)
[1,2,3,4,5,6][..3]
=> [1, 2, 3, 4]
[1,2,3,4,5,6][...3]
=> [1, 2, 3]
ROLES = %w[superadmin manager admin contact user]
ROLES[ROLES.index('admin')..]
=> ["admin", "contact", "user"]
another way is to use the range method
foo = [1,2,3,4,5,6]
bar = [10,20,30,40,50,60]
a = foo[0...3]
b = bar[3...6]
print a + b
=> [1, 2, 3, 40, 50 , 60]
I like ranges for this:
def first_half(list)
list[0...(list.length / 2)]
end
def last_half(list)
list[(list.length / 2)..list.length]
end
However, be very careful about whether the endpoint is included in your range. This becomes critical on an odd-length list where you need to choose where you're going to break the middle. Otherwise you'll end up double-counting the middle element.
The above example will consistently put the middle element in the last half.

Resources