I have three arrays: g, a and b. g has two groups of one or more consecutive nil values. The first (second) group contains a.size (b.size) nils. I wish to replace each nil in the first (second) group with the corresponding element of a (b). For example, if:
g = [1, 2, nil, nil, nil, 3, nil, nil, nil, nil]
a = [55, 45, 56]
b = [100, 200, 300, 400]
I wish g to become:
[1, 2, 55, 45, 56, 3, 100, 200, 300, 400]
How can I do that?
The []= method can assign like this, one of its variants is
foo[insertion_point, length] = values
(there is also a version that takes a range instead of this pair of values)
g = [1,2,nil,nil,nil,3,nil,nil,nil,nil]
a = [55,45,56]
b = [100,200,300,400]
g[2, a.size] = a
g #=> [1, 2, 55, 45, 56, 3, nil, nil, nil, nil]
g[6, b.size] = b
g #=> [1, 2, 55, 45, 56, 3, 100, 200, 300, 400]
As long as the gaps are the same size as the arrays you are inserting, you wouldn't need to do anything about the nils
g.map!{|e| e || a.shift || b.shift}
# => [1, 2, 55, 45, 56, 3, 100, 200, 300, 400]
I set up an enumurator and pull items from it whenever necessary. Either of these extends easily to more input lists
itr = (a + b).to_enum
g.map!{ |e| e.nil? || itr.next }
or in this case [a, b] can be replaced with a more general tree
itr = [a, b].flatten.to_enum
g.map!{ |e| e.nil? || itr.next }
I think you can do it in this way:
g.insert(6, *b); // inserts b into 6 index
g.insert(2, *a); // inserts a into 2 index
g.compact! // removes the nil elements
Note that we are inserting in the reverse way so as to not disturb the indexes.
Related
In Ruby, how can one multiply every element in one array by every element in another array, such that:
a = [1,2,3]
b = [4,5,6]
c = a*b = [4,5,6,8,10,12,12,15,18]
For a nice abstraction, can get cartesian product using product:
a.product(b).map { |aa, bb| aa * bb }
This solution makes use of Matrix methods to compute (and then flatten) the outer product of two vectors.
require 'matrix'
(Matrix.column_vector(a) * Matrix.row_vector(b)).to_a.flatten
#=> [4, 5, 6, 8, 10, 12, 12, 15, 18]
Like the other two answers to date, this produces a temporary array, which when flattened (if not already flattened) contains a.size**2 elements. If a is so large that this results in a storage problem, you could use a pair of enumerators instead:
a.each_with_object([]) { |aa,arr| b.each { |bb| arr << aa*bb } }
#=> [4, 5, 6, 8, 10, 12, 12, 15, 18]
The enumerators are as follows.
enum_a = a.each_with_object([])
#=> #<Enumerator: [1, 2, 3]:each_with_object([])>
aa, arr = enum_a.next
#=> [1, []]
aa, arr = enum_a.next
#=> [2, []]
...
enum_b = b.each
#=> #<Enumerator: [4, 5, 6]:each>
bb = enum_b.next
#=> 4
bb = enum_b.next
#=> 5
...
See Enumerator#next. This is how enumerators pass elements to their blocks.
The method Enumerable#each_with_object is very convenient and not as complex as it may initially seem. For the most part it just saves two lines of code from the following.
arr = []
a.each { |aa| b.each { |bb| arr << aa*bb } }
arr
Tried with following,
a.product(b).map { |x| x.inject(&:*) }
Amazingly following also solve it,
a.map { |x| b.map(&x.method(:*)) }.flatten
This is not beautiful but returns what you want.
a.map{|aa| b.map{|bb| bb * aa}}.flatten
I got this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
I want to sum the numeric values, so I have converted this into a single array by using flatten:
array = array.flatten
#=> ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
then:
a = []
array.each do |r|
a << r if r.class == Fixnum
end
a
This works, but I am searching for a simpler solution, maybe converting the array into an "only numeric" array first:
[20, 3, 3, 20]
Not that obvious, but you can pass a class or module to grep to select its instances:
array.grep(Integer)
#=> [20, 3, 3, 20]
or
array.grep(Numeric)
#=> [20, 3, 3, 20]
or to grep_v in order to exclude its instances:
array.grep_v(String)
#=> [20, 3, 3, 20]
This works because grep uses === for pattern matching. If you pass a class, it invokes Module#=== (each class is a module) which returns true if the object is an instance of the receiver:
Integer === 20
#=> true
Integer === 'foo'
#=> false
However, your actual problem can be solved much easier. Given this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
And assuming that the second value is always a number, you can use sum with a block:
array.sum { |string, number| number }
#=> 46
or to sum each sub-array's last value: (which is the same in this case)
array.sum(&:last)
#=> 46
One might use class’ case-equality Module#=== to determine a class of elements and to reject strings:
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.reject &String.method(:===)
#⇒ [
# [0] 20,
# [1] 3,
# [2] 3,
# [3] 20
# ]
array.delete_if { |x| x.kind_of(String) }
array.map { |item| item if item.is_a? Integer }.compact
# [20, 3, 3, 20]
More compact:
array.select { |item| item.is_a? Integer }
Or even shorter:
array.select &Integer.method(:===)
Follow the below code
array.reject { |c| c.class.name.eql?('String') }
With is_a?
array.reject { |c| c.is_a?(String) }
If your arrays are formatted like your example then this will work:
array.select.with_index { |_,i| i.odd? } #=> [20, 3, 3, 20]
use select to select only integers from the array
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.select { |tmp| tmp.is_a?(Integer) }
#=> [20, 3, 3, 20]
use select! if you want to permanently change the content of your variable array with the integer-only content
I have two different arrays. Let's say:
a = [1, 2, 13, 4, 10, 11, 43]
b = [44, 23, 1, 4, 10, 2, 55, 13]
Now I have to sort the array b by referring to the array a. I tried the following solution:
lookup = {}
a.each_with_index do |item, index|
lookup[item] = index
end
b.sort_by do |item|
lookup.fetch(item)
end
But I'm getting the KeyError: key not found: 44 error. Can anyone help me find a solution?
Expected output is [1, 2, 13, 4, 10, 23, 44, 55].
Comparing arrays checks the first value, if it's equal goes to the second value and so on. Hence this will compare by the order of occurrence in a and then by the actual value for the ones not in a:
b.sort_by { |e| [a.index(e) || a.size, e] }
To keep O(nlogn), you could:
ai = a.each_with_index.to_h
b.sort_by { |e| [ai[e] || a.size, e] }
Trying to iterate over and array and for any digit 10 or higher, split those digits and add them together for instance: 10 > "1" "0" > 1.
I am able to iterate through the array and achieve that. however, it returns nil instead of the digits < 9.
def over_ten_sum
#splits the numbers over 10 into seperate digit and sums them
square_odd.map do |num|
if num > 9
num.to_s.chars.each_slice(2).map { |num_1, num_2| num_1.to_i + num_2.to_i }
end
end
end
With a value of [6, 4, 10, 2, 14, 7, 8, 4, 6, 7, 18, 4] it returns:
=> [nil, nil, [1], nil, [5], nil, nil, nil, nil, nil, [9], nil]
I am trying to have the output be
[6, 4, 1, 2, 5, 7, 8, 6, 7, 9, 4]
Just not seeing the disconnect here. Thank you in advance for any insights.
Suppose you were to write
[1, 2, 3].map { |n| }
#=> [nil, nil, nil]
An array of nils is returned because map returns nil for n if n is not assigned a value in the block. Similarly,
[1, 2, 3].map { |n| 2*n if n > 1 }
#=> [nil, 4, 6]
which is very similar to the problem with the OP's code. If one doesn't want nils in the array returned one simply needs to map each element of the array into a non-nil value:
[1, 2, 3].map { |n| n > 1 ? 2*n : n }
#=> [1, 4, 6]
Now let's look at the line
num.to_s.chars.each_slice(2).map { |num_1, num_2| num_1.to_i + num_2.to_i }
If num = 34, this returns [7], which, except for the fact that 7 is in an array, is correct. On the other hand, if num = 134 the expression returns [4, 4] (i.e., [1+3, 4]), which I don't expect is what is wanted. If, however, the numbers always have two digits, the above expression is the same as:
num[0].to_i + num[1].to_i
which is much simpler.1 To make it more general you need to write something like the following2:
def over_nine_sum(arr)
arr.map { |n| n > 9 ? n.to_s.each_char.reduce(0) { |t,s| t + s.to_i } : n }
end
over_nine_sum [12, 5, 71, 3]
#=> [3, 5, 8, 3]
See Enumerable#reduce (aka inject).
#JörgWMittag noted (see comment) that the sum of the digits of a single-digit number (0-9) is the same as the number itself, so there is no need to treat those numbers differently. We may therefore write
def sum_digits(arr)
arr.map { |n| n.to_s.each_char.reduce(0) { |t,s| t + s.to_i } }
end
sum_digits [12, 5, 71, 3]
#=> [3, 5, 8, 3]
As #steenslag's suggested in a comment, this can be simplified to
def sum_digits(arr)
arr.map { |n| n.digits.sum }
end
which uses the methods Integer#digits and Array#sum (both new in Ruby v2.4).
Consider the steps (for the first version of sum_digits above) when n = 34:
n.to_s.each_char.reduce(0) { |t,s| t + s.to_i }
#=> 34.to_s.each_char.reduce(0) { |t,s| t + s.to_i }
#=> "34".each_char.reduce(0) { |t,s| t + s.to_i }
Now reduce initializes the block variable t (the "memo", which is returned) to zero and passes the first digit of "34" to the block and assigns it to the block variable s:
t = 0
s = "3"
The block calculation is:
t + s.to_i
#=> 0 + "3".to_i
#=> 3
which is the updated value of t. Next,
s = "4"
t + s.to_i
#=> 3 + "4".to_i
#=> 3 + 4
#=> 7
1. Another problem is that if square_odd is a local variable, Ruby will raise an "undefined variable or method" exception when it evaluates it.
2. n.to_s.each_char.reduce(0)... is preferable to n.to_s.chars.reduce(0)... because chars returns a temporary array whereas each_char returns an enumerator.
Remove the if:
def over_ten_sum
#splits the numbers over 10 into seperate digit and sums them
square_odd.map do |num|
num.to_s.chars.each_slice(2).map { |num_1, num_2| num_1.to_i + num_2.to_i }
end.flatten
end
#=> [6, 4, 1, 2, 5, 7, 8, 4, 6, 7, 9, 4]
What was wrong? if num > 9 left out every other number from being treated and nothing was returned, so you got nil each time. To make it clearer, check the following code:
def over_ten_sum
#splits the numbers over 10 into seperate digit and sums them
square_odd.map do |num|
if num > 9
num.to_s.chars.each_slice(2).map { |num_1, num_2| num_1.to_i + num_2.to_i }
else
num
end
end.flatten
end
#=> [6, 4, 1, 2, 5, 7, 8, 4, 6, 7, 9, 4]
As you can see, the result is the same, because else send num back as it is when it is not greater than 9.
Problem Question
Divisors of 42 are : 1, 2, 3, 6, 7, 14, 21, 42. These divisors squared are: 1, 4, 9, 36, 49, 196, 441, 1764. The sum of the squared divisors is 2500 which is 50 * 50, a square!
Given two integers m, n (1 <= m <= n) we want to find all integers between m and n whose sum of squared divisors is itself a square. 42 is such a number.
The result will be an array of arrays, each subarray having two elements, first the number whose squared divisors is a square and then the sum of the squared divisors.
Code below
How can I make this specific program run faster? My current code times out after n > 9999.
#returns the divisors of each number in an array of arrays
r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }
#this finds all integers between m and n whose sum of squared divisors is itself a square
squarenumbers = r.map { |x| x.map { |c| c**2 }.inject(:+) }.select { |x| Math.sqrt(x) % 1 == 0 }
#returns an array of booleans.
booleans = r.map { |x| x.map { |c| c**2 }.inject(:+) }.map { |x| Math.sqrt(x) % 1 == 0 }
#returns the index of each of the true values in booleans as an array
indexer = booleans.map.with_index{|x, i| i if x == true }.compact
#returns the numbers whose squared divisors is a square in an array
unsqr = indexer.map { |x| (m..n).to_a[x] }
#merges the two arrays together, element for element and creates an array of arrays
unsqr.zip(squarenumbers)
# for m = 1 and n = 1000 the result would be
# [[1, 1], [42, 2500], [246, 84100], [287, 84100], [728, 722500]]
Brute-force calculatioins of factors
You begin by calculating:
m, n = 40, 42
r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }
#=> [[1, 2, 4, 5, 8, 10, 20, 40], [1, 41], [1, 2, 3, 6, 7, 14, 21, 42]]
That's OK, but you don't need .to_a:
r = (m..n).map { |z| (1..z).select { |x| z % x == 0} }
#=> [[1, 2, 4, 5, 8, 10, 20, 40], [1, 41], [1, 2, 3, 6, 7, 14, 21, 42]]
This avoids an extra step, which is the creation of the temporary array1,2:
(m..n).to_a #=> [40, 41, 42]
Structure of a solution
Let's work backwards to come up with our code. First, concentrate on determining, for any given number q, if the sum of squares of the factors of q is itself a perfect square. Suppose we construct a method magic_number? which takes q as its only argument and returns true if q satisfies the required property and false otherwise. Then we will compute:
(m..n).select { |q| magic_number?(q) }
to return an array of all numbers between m and n that satisfy the property. magic_number? can be written like this:
def magic_number?(q)
return true if q == 1
s = sum_of_squared_factors(q)
s == Math.sqrt(s).round**2
end
Calculating sum of squared factors
So now we are left with writing the method sum_of_squared_factors. We can use your code to obtain the factors:
def factors(q)
(1..q).select { |x| q % x == 0 }
end
factors(40) #=> [1, 2, 4, 5, 8, 10, 20, 40]
factors(41) #=> [1, 41]
factors(42) #=> [1, 2, 3, 6, 7, 14, 21, 42]
and then write:
def sum_of_squared_factors(q)
factors(q).reduce(0) { |t,i| t + i*i }
end
sum_of_squared_factors(40) #=> 2210
sum_of_squared_factors(41) #=> 1682
sum_of_squared_factors(42) #=> 2500
Speeding the calculation of factors
There's something more we can do to speed up the calculation of factors. If f is a factor of n, f and n/f, are both factors of n. (For example, since 3 is a factor of 42, so is 42/3 #=> 14). We therefore need only obtain the smaller of each pair.
There is one exception to this rule. If n is a perfect square and f == n**0.5, then f = n/f, so we only include f among the factors of n (not n/f as well).
If turns out that if f is the smaller of the pair, f <=(n**0.5).round3. We therefore need only check to see which of the numbers (1..(n**0.5).round) are factors and include their complements (unless n is a perfect square, in which case we do not double-count (n**0.5).round):
q = 42
arr = (1..Math.sqrt(q).round).select { |x| q % x == 0 }
#=> [1, 2, 3, 6]
arr = arr.flat_map { |n| [n, q/n] }
#=> [1, 42, 2, 21, 3, 14, 6, 7]
arr.pop if a[-2] == a[-1]
arr
#=> [1, 42, 2, 21, 3, 14, 6, 7]
q = 36
arr = (1..Math.sqrt(q).round).select { |x| q % x == 0 }
#=> [1, 2, 3, 4, 6]
arr = arr.flat_map { |n| [n, q/n] }
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6, 6]
arr.pop if a[-2] == a[-1]
#=> 6
arr
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6]
so we can write:
def factors(q)
arr = (1..Math.sqrt(q)).select { |x| q % x == 0 }
arr = arr.flat_map { |n| [n, q/n] }
arr.pop if arr[-2] == arr[-1]
arr
end
Substituting out arr ("chaining" expressions), we obtain a typical Ruby expression:
def factors(q)
(1..Math.sqrt(q)).select { |x| q % x == 0 }.
flat_map { |n| [n, q/n] }.
tap { |a| a.pop if a[-2] == a[-1] }
end
factors(42)
#=> [1, 42, 2, 21, 3, 14, 6, 7]
factors(36)
#=> [1, 36, 2, 18, 3, 12, 4, 9, 6]
See Enumerable#flat_map and Object#tap. (There's no need for this array to be sorted. In applications where it needs to be sorted, just tack .sort onto the end of flat_maps block.)
Wrapping up
In sum, we are left with the following:
def magic_number?(q)
return true if q == 1
s = sum_of_squared_factors(q)
s == Math.sqrt(s).round**2
end
def sum_of_squared_factors(q)
factors(q).reduce(0) { |t,i| t + i*i }
end
def factors(q)
(1..Math.sqrt(q)).select { |x| q % x == 0 }.
flat_map { |n| [n, q/n] }.
tap { |a| a.pop if a[-2] == a[-1] }
end
m, n = 1, 1000
(m..n).select { |q| magic_number?(q) }
#=> `[1, 42, 246, 287, 728]
This calculation was completed in a blink of an eye.
Compute primes to further speed calculation of factors
Lastly, let me describe an even faster way to compute the factors of a number, using the method Prime::prime_division. That method decomposes any number into its prime components. Consider, for example, n = 360.
require 'prime'
Prime.prime_division(360)
#=> [[2, 3], [3, 2], [5, 1]]
This tells us that:
360 == 2**3 * 3**2 * 5**1
#=> true
It also tells us that every factor of 360 is the product of between 0 and 3 2's, multiplied by between 0 and 2 3's, multiplied by 0 or 1 5's. Therefore:
def factors(n)
Prime.prime_division(n).reduce([1]) do |a,(prime,pow)|
a.product((0..pow).map { |po| prime**po }).map { |x,y| x*y }
end
end
a = factors(360).sort
#=> [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18,
# 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360]
We can check that:
a == (1..360).select { |n| (360 % n).zero? }
#=> true
One other check:
factors(40).sort
#=> [1, 2, 4, 5, 8, 10, 20, 40]
1. You could instead write that [*m..n] #=> [40, 41, 42].
2. Why is it not necessary to convert the range to an array? Enumerable#map, being an instance method of the module Enumerable, is available for use by every class that includes Enumerable. Array is one, but (m..n).class #=> Range is another. (See the second paragraph at Range).
3. Suppose f is smaller than n/f and f > n**0.5, then n/f < n/(n**0.5) = n**0.5 < f, a contradiction.
I don't know Ruby but the problem lies with the algorithm used in finding the divisors of a number (which is not specific to the language used, i.e. Ruby in this case).
r = (m..n).to_a.map { |z| (1..z).select { |x| z % x == 0} }
To find the divisors of an integer n you are dividing n by all positive integers unto n - 1 which means the loop runs n - 1 times. However, it is enough to divide upto sort(n) to calculate the divisors. In pseudocode this looks like below:
for i = 1 to i <= sqrt(n)
r = n % i
if r == 0 then
i is a divisor
if n / i != i then
n / i is another divisor
For example:
sqrt_42 = 6.48074069840786
i = 1 => 1 and 42 are two divisors
i = 2 => 2 and 21
i = 3 => 3 and 14
i = 4 => no divisor
i = 5 => no divisor
i = 6 => 6 and 7
And thats all.
This will improve the performance a lot since now the loop runs only sort(n) times instead of n - 1 times which is a big difference for large n.