How to subtract arrays of integers - arrays

I have four arrays of int:
num_defect = [30, 30, 20, 20, 18, 18, 5, 5]
num_fixes = [1, 0, 3, 2, 1, 2, 2, 2]
num_blocks = [0, 0, 0, 0, 2, 2, 1, 0]
num_ext_defects = [1, 1, 0, 0, 2, 2, 2, 1]
I want to display the number of open defects, which is given by:
num_defects - num_fixes - num_blocks - num_ext_defects
So for the reporting, num_defects should now contain:
[28, 29, 17, 13, 12, 0, 2]
I tried:
num_defect.map { |i| i - num_fixes[i] - num_blocks[i] - num_ext_defects[i] }
but it raises:
nil can't be coerced into Fixnum
Any help greatly appreciated.

With
num_defect.map { |i|
i is the element of the array, not its index. If you want your map to work correctly, you'll need an index as well:
num_defect.map.with_index do |element, index|
element - num_fixes[index] - num_blocks[index] - num_ext_defects[index]
end
Use map! instead of map in order to mutate num_defect.
Or if you'd like a nicer version:
a = [30,30,20,20,18,18,5,5]
b = [ 1, 0, 3, 2, 1, 2,2,2]
c = [ 0, 0, 0, 0, 2, 2,1,0]
d = [ 1, 1, 0, 0, 2, 2,2,1]
a.zip(b,c,d).map { |arr| arr.inject(:-) }
# => [28, 29, 17, 18, 13, 12, 0, 2]

If i understand you correctly you might be looking for an array method called each_index.
num_defect.each_index do |i|
num_defect[i] -= num_fixes[i] + num_blocks[i] + num_ext_defects[i]
end

require 'matrix'
(Vector.elements(num_defect) - Vector.elements(num_fixes) -
Vector.elements(num_blocks) - Vector.elements(num_ext_defects)).to_a
#=> [28, 29, 17, 18, 13, 12, 0, 2]
This uses the methods Vector::elements and Vector#to_a. One could write Vector[*arr] in place of Vector.elements(arr), using Vector::[].
If num_defect is to be mutated, you could write num_defect.replace(<above expression>). If
arr = [num_defect, num_fixes, num_blocks, num_ext_defects]
#=> [[30, 30, 20, 20, 18, 18, 5, 5],
# [ 1, 0, 3, 2, 1, 2, 2, 2],
# [ 0, 0, 0, 0, 2, 2, 1, 0],
# [ 1, 1, 0, 0, 2, 2, 2, 1]]
one could use matrix multiplication:
(Matrix.row_vector([1, *[-1]*(arr.size-1)]) * Matrix.rows(arr)).to_a.first
#=> [28, 29, 17, 18, 13, 12, 0, 2]
where
[1, *[-1]*(arr.size-1)]
#=> [1, -1, -1, -1]
This would be convenient and relatively computationally-efficient if arr had a larger number of elements than it does in the example.
This uses the Matrix methods Matrix::row_vector, Matrix::rows and Matrix#to_a. One could write Matrix[*arr] in place of Matrix.rows(arr), using Matrix::[]. One advantage of using rows, however, is that one can add the argument false (Matrix.rows(arr, false)) to avoid copying the elements of arr in the creation of the Matrix object.

[num_defect, num_fixes, num_blocks, num_ext_defects]
.transpose
.map{|first, *rest| first - rest.sum}
# => [28, 29, 17, 18, 13, 12, 0, 2]

Using Enumerator#each_with_object:
num_defect.each_with_index.with_object([]){ |(e, i), a| a << (e - num_fixes[i] - num_blocks[i] - num_ext_defects[i]) }
#=> [28, 29, 17, 18, 13, 12, 0, 2]

Related

Identify rows of one numpy array that gave rise to another numpy array

I think I'm missing something obvious. Consider the following code:
import numpy as np
a = np.array([[ 0, 2, 0, 5, 4, 6, 2, 4],
[ 3, 4, 0, 1, 0, 7, 4, 6],
[ 2, 6, 3, 5, 2, 5, 5, 8],
[ 0, 1, 0, 8, 0, 5, 8, 10],
[ 7, 9, 2, 7, 0, 6, 7, 2],
[ 0, 1, 4, 9, 0, 7, 9, 9],
[ 0, 6, 7, 5, 6, 2, 4, 13],
[ 0, 1, 1, 4, 1, 3, 2, 3]]
# isolate columns 2,3,6,7
mask = [False,False, True, True,False,False, True, True]
b = a[:,mask]
# determine rows of b having unique elements
s = np.sort(b, axis=1)
c = b[~(s[:,:-1] == s[:,1:]).any(1)]
c looks like:
c = [[ 0, 5, 2, 4],
[ 0, 1, 4, 6],
[ 7, 5, 4, 13],
[ 1, 4, 2, 3]]
QUESTION: How do I 'recover' the rows of a that gave rise to the rows of c?
The output should be like:
d = [[ 0, 2, 0, 5, 4, 6, 2, 4],
[ 3, 4, 0, 1, 0, 7, 4, 6],
[ 0, 6, 7, 5, 6, 2, 4, 13],
[ 0, 1, 1, 4, 1, 3, 2, 3]]

How to get min/max value indices in a Ruby array

arr = [4, 9, 0, -3, 16, 7]
Is there any simple way to find the indicies of the lowest x elements? Something like this?
arr.min_index(4)
arr.each_index.min_by(x) { |i| arr[i] }
or
arr.each_with_index.min(x).map(&:last)
Demo:
> arr, x = [4, 9, 0, -3, 16, 7], 4
=> [[4, 9, 0, -3, 16, 7], 4]
> arr.each_index.min_by(x) { |i| arr[i] }
=> [3, 2, 0, 5]
> arr.each_with_index.min(x).map(&:last)
=> [3, 2, 0, 5]
Here's one simple way to do it:
class Array
def min_index(n)
each_with_index.sort.map(&:last).first(n)
end
end
>> arr = [4, 9, 0, -3, 16, 7]
>> arr.min_index(4)
#> [3, 2, 0, 5]
>> [4, 2, 2].min_by(2)
#> [1, 2]

How to extract integer values from Array (of mixed types: integers/strings) via RegExp?

I have an array:
arr2 = [6, '(7,0)', '(15,0)', '(5,0)', 3, '(15,2)', 17]
I want to parse each element of the array and get the value as follows:
arr2 = [6, 0, 0, 0, 3, 2, 17]
means if the array element is like (15,2) then only the second element i.e 2 should get printed in the response, and if the array element is not in the format like (7,0) then it should be printed as it is.
arr = [6, '(7,0)', '(15,0)', '(5,0)', 3, '(15,2)', 17]
arr.map do |obj|
case obj
when Integer
obj
else
obj[/(?<=,)\d+/].to_i
end
end
#=> [6, 0, 0, 0, 3, 2, 17]
[6, [7,0], [15,0], [5,0], 3, [15,2], 17].map { |e| [*e].last }
#⇒ [6, 0, 0, 0, 3, 2, 17]
If the elements are strings:
%w[6, (7,0), (15,0), (5,0), 3, (15,2), 17].map { |e| e[/\d+(?=\)|,?\z)/] }
#⇒ ["6", "0", "0", "0", "3", "2", "17"]
map(&:to_i) the latter to get an array of integers.
Finally, for the up-to-date version:
[6, '(7,0)', '(15,0)', '(5,0)', 3, '(15,2)', 17].
map { |e| e.to_s[/\d+(?=\)|,?\z)/] }.
map(&:to_i)
#⇒ [6, 0, 0, 0, 3, 2, 17]
Exotics:
[6, '(7,0)', '(15,0)', '(5,0)', 3, '(15,2)', 17].
inspect.
scan(/\d+(?:,\d+)?/).
map { |e| e.split(',').last.to_i }
#⇒ [6, 0, 0, 0, 3, 2, 17]
This seems fine to me:
arr2.map{|x| x.to_s.scan(/\d+/).last.to_i }
And if you want something without regexp:
arr2.map do |item|
item.is_a?(Integer) ? item : item[1+item.rindex(',')..-2].to_i
end

Group an array of integers in a hash of ranges and counts

I have an array of integers like this:
[1, 1, 1, 2, 2, 4, 4, 5, 6, 11, 11, 12, 15, 22, 23, 23, 23, 31, 32, 32]
I am trying to convert this to a hash, grouping according to ranges at intervals of 10....
So, in this case it would be
{ [1..10] => 9, [11..20] => 4, [21..30] => 4, [31..40] => 3 }
I have tried a few things which haven't come close so it's a bit pointless putting them down here. I can convert the array to ranges
[1, 1, 1, 2, 2, 4, 4, 5, 6, 11, 11, 12, 15, 22, 23, 23, 23, 31, 32, 32].sort.uniq.inject([]) do |spans, n|
if spans.empty? || spans.last.last != n - 1
spans + [n..n]
else
spans[0..-2] + [spans.last.first..n]
end
end
But this is not what I am looking for. Any suggestions?
Hash[
your_array.group_by{|i| i / 10}.map{|k,v|
[(k*10+1..(k+1)*10), v.count]
}
]
#=> {1..10=>9, 11..20=>4, 21..30=>4, 31..40=>3}
arr.each_with_object(Hash.new(0)) do |e, hash|
i = e / 10
hash[i*10+1..i*10+10] += 1
end
#⇒ {
# 1..10 => 9,
# 11..20 => 4,
# 21..30 => 4,
# 31..40 => 3
# }
I've modified the example to have no numbers between 21 and 30, so that the hash should include the key-value pair 21..30=>0.
arr = [1, 1, 1, 2, 2, 4, 4, 5, 6, 11, 11, 12, 20, 32, 33, 33, 33, 41, 42, 42]
intervals = (1..arr.last-1).step(10).each_with_object({}) { |n,h| h[n..n+9] = 0 }
#=> {1..10=>0, 11..20=>0, 21..30=>0, 31..40=>0, 41..50=>0}
arr.each_with_object(intervals) do |n,intervals|
interval_end = 10*((n+9)/10)
intervals[interval_end-9..interval_end] += 1
end
#=> {1..10=>9, 11..20=>4, 21..30=>0, 31..40=>4, 41..50=>3}

array name stored in array, pushing to array - not working as expected

#board = {1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0, 8=>"b", 9=>"r", 10=>"u", 11=>"c", 12=>0, 13=>0, 14=>"d", 15=>"h", 16=>"s", 17=>"l", 18=>0, 19=>0, 20=>"o", 21=>"i", 22=>"l", 23=>"b", 24=>0, 25=>0, 26=>"g", 27=>"t", 28=>"f", 29=>"e", 30=>0, 31=>0, 32=>0, 33=>0, 34=>0, 35=>0, 36=>0}
#words = {"shift"=>[16, 15, 21, 28, 27], "bell"=>[23, 29, 22, 17], "curb"=>[11, 10, 9, 8], "dog"=>[14, 20, 26]}
#col1 = [8, 14, 20, 26]
#col2 = [9, 15, 21, 27]
#col3 = [10, 16, 22, 28]
#col4 = [11, 17, 23, 29]
#rcol1 = Array.new
#rcol2 = Array.new
#rcol3 = Array.new
#rcol4 = Array.new
#cols = [#col1, #col2, #col3, #col4]
#rcols0 = [#rcol1, #rcol2, #rcol3, #rcol4]
out = #words["dog"]
out.each do |remove|
#board[remove] = 100
end
def colclone
nums = 0
#rcols0.each do |fixy|
a = #cols[nums]
a.each do |stick|
fixy[nums].push #board[stick]
end
nums += 1
end
end
Here's the actual code. I tried to simplify but I think I must have left something out.
I am working on a board game. What I want is
--> #rcol1 = [0, 1, 2, 3]
what I am getting is those results in my reference array instead
--> #rcols0 = [[0, 1, 2, 3], ...]
Any help would be greatly appreciated!
You have the wrong idea, or the wrong vocabulary, about how it works. names does not store the names of the arrays. names stores a reference to the Array objects. It doesn't matter what they're named.
array1 = [0, 1, 2]
array2 = [10,20,30]
names = [array1, array2]
# names[1] contains a reference to array2
# So this is the same as array2[2]
puts names[1][2] # 30
Your code works fine.
names.each do |update|
update.push 3
update.push 4
end
puts array1.inspect # [0, 1, 2, 3, 4]
Your solution seems to be working:
array1 = [1,2,3]
=> [1, 2, 3]
array2 = [4,5,6]
=> [4, 5, 6]
names = [array1, array2]
=> [[1, 2, 3], [4, 5, 6]]
names.each do |update|
update.push 8
update.push 9
end
=> [[1, 2, 3, 8, 9], [4, 5, 6, 8, 9]]
array1
=> [1, 2, 3, 8, 9]
array2
=> [4, 5, 6, 8, 9]
I think something like this is what you're looking for:
array1 = [0, 1, 2]
array2 = [10, 11, 12]
array3 = [20, 21, 22]
names = [array1, array2, array3]
names.each do |array|
i = array[array.length-1] + 1
array.push(i,i+1)
end
p array1
p array2
p array3
p names
Output for the above is:
[0, 1, 2, 3, 4] #array1
[10, 11, 12 ,13, 14] #array2
[20, 21, 22, 23, 24] #array3
[[0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23, 24]] #names array
Modify and/or extend the code to suit your needs.

Resources