How to sum numbers in nested arrays in Ruby? - arrays

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

Related

2-D Array Pyramid in Ruby?

def adjacent_sum(arr)
narr = []
l = arr.length
arr.each.with_index do |num,index|
if index < arr.size-1
narr << arr[index] + arr[index+1]
end
end
return narr
end
print adjacent_sum([3, 7, 2, 11]) #=> [10, 9, 13], because [ 3+7, 7+2, 2+11 ]
puts
print adjacent_sum([2, 5, 1, 9, 2, 4]) #=> [7, 6, 10, 11, 6], because [2+5, 5+1, 1+9, 9+2, 2+4]
puts
Write a method pyramid_sum that takes in an array of numbers representing the base of a pyramid. The function should return a 2D array representing a complete pyramid with the given base. To construct a level of the pyramid, we take the sum of adjacent elements of the level below.
I understand that I must make a 2-D array and use adjacent addition to build the next level of the pyramid but I don't understand the basic mechanics or thinking methodology in Ruby for two-dimensional arrays. Why am I getting array and what approach do you suggest?
base = [1, 2, 3, 4, 5]
(base.size-1).times.with_object([base]) do |_,arr|
arr << arr.last.each_cons(2).map(&:sum)
end.reverse
#=> [
# [48]
# [20, 28],
# [8, 12, 16],
# [3, 5, 7, 9],
# [1, 2, 3, 4, 5],
# ]
See Enumerable#each_cons, a lovely method.

Multiply each item in an array by each item in another array

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

How do I find the size of the longest subarray?

I'm using Ruby 2.4. I have an array of arrays
arr = [[1, 8, 9, 10], [2, 3, 7], [0, 2, 15, 4, 27, 3], [2]]
How do I find the maximum number of elements for all of the child arrays? For example, in the above, the answer would be 6 since the third array has 6 elements, more than the number of elements for the other arrays.
It's so simple
arr.max_by(&:size).size
=> 6
It's really straightforward, even if you don't know max_by:
arr.map(&:size).max
#=> 6
You can use max_by on the array:
array.max_by { |i| i.length }.length
Given:
> arr=[[1, 8, 9, 10], [2, 3, 7], [0, 2, 15, 4, 27, 3], [2]]
Use max_by:
> arr.max_by {|l| l.length}
=> [0, 2, 15, 4, 27, 3]
Or, the shortcut form:
> arr.max_by(&:length)
=> [0, 2, 15, 4, 27, 3]
And if you want 6 vs the actual array:
> arr.max_by {|l| l.length}.length
=> 6
max_by is the way to go here but you can use max too:
arr.max { |a, b| a.size <=> b.size }.size
#=> 6
Benchmarks
require 'fruity'
arr = Array.new(10000) { |arr| Array.new(rand 10) }
compare do
maxby_size { arr.max_by(&:size).size }
maxby_count { arr.max_by(&:count).count }
map_max { arr.map(&:size).max }
max { arr.max { |a,b| a.size <=> b.size }.size }
reduce { arr.reduce(0) { |memo, a| memo > a.length ? memo : a.length } }
end
#Running each test 2 times. Test will take about 1 second.
#map_max is faster than max by 2x ± 0.1
#max is similar to reduce
#reduce is similar to maxby_size
#maxby_size is similar to maxby_count

iterate over array to find int over 10 and add those two digits together

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.

Comparing array elements including duplicates

I am trying to see if an array contains each element of another array. Plus I want to account for the duplicates. For example:
array = [1, 2, 3, 3, "abc", "de", "f"]
array contains [1, 2, 3, 3] but does not contain [2, 2, "abc"] - too many 2's
I have tried the below but obviously doesn't take into account the dupes.
other_arrays.each { |i| array.include? i }
This method iterates once over both arrays.
For each array, it creates a hash with the number of occurences of each element.
It then checks that for every unique element in subset, there are at least as many elements in superset.
class Array
def count_by
each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end
def subset_of?(superset)
superset_counts = superset.count_by
count_by.all? { |k, count| superset_counts[k] >= count }
end
end
[1, 2, 3, 3, "abc", "de", "f"].count_by
#=> {1=>1, 2=>1, 3=>2, "abc"=>1, "de"=>1, "f"=>1}
[1, 2, 3, 3].count_by
#=> {1=>1, 2=>1, 3=>2}
[1, 2, 3, 3].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> true
[2, 2, "abc"].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> false
If you don't want to patch the Array class, you could define :
count_by(array) and subset_of?(array1, array2).
You could first create a useful instance method for the class Array:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Then all elements of an array a are contained in array b if the following method returns true.
def subarray?(a,b)
a.difference(b).empty?
end
For example,
subarray? [1,2,3], [1,4,"cat",3,2]
#=> true
subarray? [1,2,3], [1,4,"cat",3,5]
#=> false
I've found Array#difference has such wide application that I proposed that it be added to the Ruby core. Details about the method and its uses can be found at the link and also in my answer to this SO question.

Resources