Array of integers into array of ranges - ruby

I'm trying to figure out how I can change array of integers into array of ranges.
For example I want to take this array:
ar = [0, 49, 14, 30, 40, 23, 59, 101]
into
ar = [0..49, 14..30, 40..23, 59..101]
Given array always will be even. I want to take each two values as borders of ranges.
I have tried to seperate it for two arrays. One with odd indexes second with even.
a = ar.select.with_index{|_,i| (i+1) % 2 == 1}
b = ar.select.with_index{|_,i| (i+1) % 2 == 0}
I don't have an idea how to use them to create ranges, also I would like to avoid creating redundant variables like a and b.
I don't want to sort any values. Range 40..23 is intentional.

ar.each_slice(2).map { | a, b | a..b }

I would do as #undur_gongor's suggests, but here's another way:
e = ar.to_enum
b = []
loop do
b << (e.next..e.next)
end
b
#=> [0..49, 14..30, 40..23, 59..101]

new_ar = []
ar.each_slice(2) do |r|
new_ar << Range.new(r[0], r[1])
end

Related

populate array with random numbers

I have the following array:
nums = [10, 20, 30, 40, 50]
and another array that must have 3 items:
arr = [1]
How can I get one or two items from nums array (random) to populate arr that must have 3 elements?
Use Array.sample
arr.concat num.sample(2)
nums = [10, 20, 30, 40, 50]
arr = [1]
2.times { arr << nums.sample }
Instead of sample(2) it's better to use sample without argument I think
Because sample with argument returns array with not repeated elements (with not repeated indices to be more precise)
That is, it is more random :)
Read more
You can shuffle the array and take the first three elements to ensure you don't have repeats.
arr = nums.shuffle.take(3)
If, for instance, you were dealing with a situation like a card game where you need to draw from a shuffled deck more than once, you might want to store the shuffled array, and then pop from it.
shuffled = arr.shuffle
arr = shuffled.pop(3)
# shuffled now only has the remaining two elements
Here are two ways to obtain a statistically random sample (subject to the impossibility of computing a truly random number).
The first is to select three elements at random and accept it if it contains at most two distinct elements. If it contains three distinct elements reject it and try again.
def doit(arr)
loop do
a = Array.new(3) { arr.sample }
return a if a.uniq.size <= 2
end
end
nums = [10, 20, 30, 40, 50]
doit nums #=> [40, 40, 10]
doit nums #=> [20, 50, 20]
doit nums #=> [50, 50, 20]
The second way is to make a probability calculation. Firstly, I assume that nums contains unique elements. (That assumption is not a deal-breaker but it avoids the messiness of dealing with the more complex case.)
n = nums.size
= 5
First compute the probability of drawing one distinct element (e.g., [30, 30, 30]).
P[N1] = (1/n)(1/n)
= (1/5)(1/5)
= 1/25
= 0.04
Note the first element drawn can be anything, then the second and third draws must match the first draw, the probability of each being 1/5.
Next compute the probability of drawing three distinct elements:
P[N3] = ((n-1)/n)*((n-2)/n)
= (n-1)*(n-2)/n**2
= 4*3/5**2
= 12/25
= 0.48
Again, the first element can be anything. The probability of the second element drawn being different is (n-1)/n and the probability of the third element drawn being different than the first two
is (n-1)/n.
Lastly, if N2 is the event in which exactly two of the three elements drawn are unique we have
P[N1] + P[N2] + P[N3] = 1
so
P[N2] = 1 - P[N1] - P[N3]
= 1 - 0.04 - 0.48
= 0.48
We therefore may compute
P[N1|N1 or N2] = P[N1 and (N1 or N2)]/P[N1 or N2]
P[N1|N1 or N2] = P[N1]/P[N1 or N2]
= 0.04/(0.04 + 0.48)
= 0.04/0.52
= 0.0769
Therefore,
P[N2|N1 or N2] = 1 - P[N1|N1 or N2]
= 1 - 0.0769
= 0.9231
We therefore may write the following to obtain a statistically random sample.
def doit(arr)
if rand < 0.0769
n = arr.sample
[n, n, n]
else
n1 = arr.sample
n2 = (arr - [n1]).sample
[n1, n2, n2]
end
end
doit nums #=> [40, 40, 40]
doit nums #=> [10, 20, 20]
doit nums #=> [10, 20, 20]

Intersection of two dimensional array

Is there a simple way to find the intersection of a two dimensional array? For example:
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr1,arr3]
I know that it's possible to do:
intersection = arr1 & arr2 & arr3 # => 5
intersection = big_arr[0] & big_arr[1] & big_arr[2] # => 5
but the number of elements in big_arr will vary. I was wondering if there was a simple way to intersect all the elements in big_arr regardless of the number of elements.
Use #reduce like
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr2,arr3]
bigarr.reduce(:&) # => [5]
What do you want: a method with a pretty face or one that is first to finish line? My friend #Arup has supplied one; I'll offer the another.
Code
def heavy_lifter(a)
wee_one = a.min_by(&:size)
return [] if wee_one.empty?
wee_loc = a.index(wee_one)
counts = wee_one.each_with_object({}) { |e,h| h.update(e=>1) }
nbr_reqd = 1
a.each_with_index do |b,i|
next if i == wee_loc
b.each do |e|
cnt = counts[e]
case
when cnt.nil?
next
when cnt == nbr_reqd
counts[e] = cnt + 1
when cnt < nbr_reqd
counts.delete(e)
return [] if counts.empty?
end
end
nbr_reqd += 1
end
counts.keys.each { |k| counts.delete(k) if counts[k] < nbr_reqd }
counts.keys
end
Example
a1 = [1,2,3,4,5]
a2 = [5,6,7,8]
a3 = [5]
a = [a1,a2,a3]
heavy_lifter(a)
#=> [5]
Explanation
Here's how the method works:
select the smallest array (wee_one). To simplify the explanation, assume it is the first element of a.
convert wee_one to a counting hash, counts, where counts[e] = 1 for each element of wee_one.
iterate through the remaining arrays.
keys of counts will be removed as arrays are processed.
after all calculations are complete, counts.keys equals the intersection of all arrays.
after nbr_reqd arrays have been processed (including wee_one), counts[k] equals the number of those arrays that have been found to contain k. Obviously, if counts[k] < nbr_reqd, key k can be removed from counts (but we will not remove such keys until our attention is drawn to them, or at the end).
suppose we are now to process the array b at offset nbr_reqd, meaning nbr_reqd arrays have been processed (including wee_one at offset zero). For each element e of b, we obtain cnt = counts[e]. There are four possibilities:
cnt == nil, in which case there is nothing to be done;
cnt < nbr_reqd, in which case key e is removed from counts;
cnt == nbr_reqd, meaning e has been present in all previous arrays processed, in which case we execute counts[k] = cnt + 1; and
cnt == nbr_read+1, meaning e has been present in all previous arrays processed and is a duplicate of another e in b that has already been processed, in which case nothing is to be done.
nbr_reqd is incremented by one and the process is repeated for the next array.
after all arrays have been processed, all that remains is to remove each key k in counts for which counts[k] < nbr_reqd.
Cutie method
def cutie(a)
a.reduce(:&)
end
Test data
def test(mx, *sizes)
sizes.map { |sz| Array.new(sz) { rand(mx) } }
end
For example:
test(10,5,6,7)
#=> [[9, 1, 5, 1, 1], [0, 8, 7, 8, 5, 0], [5, 1, 7, 6, 7, 9, 5]]
Benchmark code
require 'benchmark'
def bench(tst)
Benchmark.bm(12) do |bm|
bm.report 'cutie' do
cutie(tst)
end
bm.report 'heavy_lifter' do
heavy_lifter(tst)
end
end
end
Benchmark results
tst = test(1_000_000, 400_000, 600_000, 800_000)
cutie(tst).size
#=> 81929
cutie(tst).sort == heavy_lifter(tst).size
#=> true
bench(tst)
user system total real
cutie 1.610000 0.030000 1.640000 ( 1.639736)
heavy_lifter 1.800000 0.020000 1.820000 ( 1.824281)
sizes = (700_000..890_000).step(10_000).to_a
#=> [700000, 710000, 720000, 730000, 740000,
# 750000, 760000, 770000, 780000, 790000,
# 800000, 810000, 820000, 830000, 840000,
# 850000, 860000, 870000, 880000, 890000]
tst = test(1_000_000, *sizes)
bench(tst)
user system total real
cutie 14.090000 0.440000 14.530000 ( 14.679101)
heavy_lifter 5.830000 0.030000 5.860000 ( 5.935438)

How do I populate an array with random numbers?

I am trying to populate an array of four elements with positive integers that are less than 9.
Here is my code:
generated_number=Array.new(4)#create empty array of size 4
generated_number.each do |random| #for each position in the array create a random number
random=rand(10)
end
puts generated_number
I don't understand what I'm missing.
You can pass a range to rand()
Array.new(4) { rand(1...9) }
I think you're over complicating things.
generated_numbers = 4.times.map{Random.rand(8) } #=> [4, 2, 6, 8]
edit: For giggles I put together this function:
def rand_array(x, max)
x.times.map{ Random.rand(max) }
end
puts rand_array(5, 20) #=> [4, 13, 9, 19, 13]
This is how I solved it for an array with 10 elements:
n=10
my_array = Array.new(n)
i = 0
loop do
random_number = rand(n+1)
my_array.push(random_number)
i += 1
break if i >= n
end
for number in my_array
puts number
This is something I did for a school final, numbers aren't exactly the same but you can change numbers and such:
numbers_array = []
10.times do
numbers_array.push(rand(1..100))
end
puts numbers_array

Finding the index of second smallest number in the list?

How can I find the second smallest number and return its index?
Another approach :
>> a = [1,3,5,6,2,4]
=> [1, 3, 5, 6, 2, 4]
>> a.index(a.sort[1])
=> 4
>>
I can see two options from the top of my head:
Delete the current min, so the new min will be the previous second min
arr = num.delete(num.min)
min_bis = arr.min
Loop through the array, using 2 variables to store the 2 lowest values.
This might be a little trickier but the complexity would only be O(n).
I don't know why you don't want to sort the array, but if it's a performance issue, it's probably one of the best options (to sort it) especially if the array is small.
(Below, Enumerable is a superset of Array, Hash and Range etc.)
Enumerable#sort returns a fresh array containing all the elements of the original object in a sorted order, so you can write a = num.sort[1] (provided that l > 1) to find the second smallest number, without modfying the original input nums.
Then you can feed it to Enumerable#find_index.
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-sort
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find_index
By the way
while (index <l)
nums - gets.to_i
num[index] = nums
index +=1
end
can be written as
nums = (0...l).map{ gets.to_i }
I understand you don't want to sort the array before finding the second-lowest number. But are you willing to use a sorted clone/copy of that array?
If
nums = [1, 5, 1, 9, 3, 8]
Then:
# grab a sorted copy of nums
b = nums.sort
# b = [1, 1, 3, 5, 8, 9]
# delete the lowest number
b.delete(b.min)
# now b = [3, 5, 8, 9]
# so get the index from the original array
nums.index(b.first)
which should return 4 because nums[4] = 3. (You could also use nums.index(b[0]) since b is already sorted.)
If you don't mind being destructive to the original array:
a.delete(a.min)
a.index(a.min)
Here's an approach that does not use sort:
arr = [3,1,2,5,1]
If second_smallest(arr) => 2 is desired:
def second_smallest(arr)
return nil if arr.uniq.size < 2
mn, mx = arr.min, arr.max
arr.map! { |e| e == mn ? mx : e }
arr.index(arr.min)
end
If second_smallest(arr) => 4 is desired:
def second_smallest(arr)
return nil if arr.uniq.size < 2
i1 = arr.index(arr.min)
arr.delete_at(i1)
i2 = arr.index(arr.min)
i2 >= i1 ? i2 + 1 : i2
end
You don't want to use sort as it's O(nlogn). You want to iterate through the array only once (after getting the max):
arr = [123,35,12,34,5,32]
This is a straight forward way of solving the problem:
def second_min_index(arr)
max = arr.max
min, min_index, second_min, second_min_index = max, 0, max, 0
arr.each_with_index do |e, i|
# if something is less than min, second min should become what used to be min
if (e <= min)
second_min, second_min_index, min, min_index = min, min_index, e, i
# if something is less than second min (but not less than min)
# it becomes the second min
elsif (e < second_min)
second_min, second_min_index = e, i
end
end
second_min_index
end
second_min_index(arr)
=> 2
A better and more reusable way of doing this would be via a transform and conquer solution (just in case you get asked for the 3rd smallest number):
def min_index(arr)
min, min_index = arr[0], 0
arr.each_with_index { |e,i| min, min_index = e,i if e < min }
min_index
end
def min_index_excluding(arr, exclude_indexes)
min, min_index = arr[0], 0
arr.each_with_index { |e,i| min, min_index = e,i if (e < min && !exclude_indexes.include?(i)) }
min_index
end
def second_min_index(arr)
min_index_excluding(arr, [min_index(arr)])
end
second_min_index(arr)
=> 2
a_sorted = a.sort
second_min = a_sorted[1]
a.index(second_min)

Rails 3 - Remove items belonging to array B from array A

I'm starting to play with arrays, but i'm stuck with something that seems yet very simple...
I'm trying to remove x elements belonging to one array from another array.
I've looked at this but .... blocked :
Deleting items from an array requires multiple passes to remove them all
a = ["1","2","3","4","5","6"]
b = ["1","3"]
c = a.reject { |i| i =~ /b/ }
Well, i'm a bit lot here, thanks!
a = ["1","2","3","4","5","6"]
b = ["1","3"]
c = a - b
same as
c = a.reject{ |e| b.include? e }
If you want to modify an existing array by removing elements in another array you can use minus equals.
a = [1, 2, 3, 1, 4]
b = [1, 4]
a -= b
a
=> [2, 3]
Also keep in mind that subtracting an array of elements from another array will remove all occurrences of those elements not just the first occurrence.

Resources