Ruby search for the same values in multidimensional array - arrays

I have an array with many arrays inside (2d) (in this example there are four of them):
[
[13, 15, 18, 23, 23, 11, 14, 19, 19, 5, 10, 10, 8, 8],
[8, 15, 19, 21, 21, 12, 16, 18, 18, 11, 13, 13, 6, 6],
[9, 15, 21, 23, 23, 7, 13, 15, 15, 12, 14, 14, 8, 8],
[2, 8, 14, 16, 16, 7, 13, 15, 15, 12, 14, 14, 8, 8]
]
I need to find if any element on any of these arrays is the same and at the same index as in other array. I need to get all those numbers and their indexes.
For ex. First_array[1] = 15, as well as second_array[1] = 15 and third_array[1] = 15. So I need these, with their indexes.
Also all needed values must come from arrays that are to the left or to the right to the array. For ex. - array_one[3] = 23, array_two[3] = 21 and array_three[3] = 23. I dont need these since array_two has a different value and it separates array_one from array_three.
And What I can get is the length of arrays (they all the same length) and the number of arrays, as variables.
I hope you got my point :)
Looks like I am abit closer to my goal. It seems this checks well for the second array (so only two first arrays being checked, but if this was done, the rest should be much easier). And do not judge me, judge just the code :D I know its ugly, its just a prototype:
array.each do |c|
c.each do |v|
c.each_with_index do |k, i|
next_array = array[i + 1]
if next_array.include? v
its_index = next_array.index(v)
if c.index(v) == its_index
p v
end
end
break
end
end
return
end

arr = [[13, 15, 18, 23, 23, 11, 14, 19, 19, 5, 10, 10, 8, 8],
[ 8, 15, 19, 21, 23, 12, 16, 18, 19, 11, 13, 13, 6, 8],
[ 9, 15, 21, 23, 16, 12, 13, 15, 15, 12, 14, 14, 8, 8],
[ 2, 8, 14, 21, 16, 7, 13, 15, 15, 12, 14, 14, 8, 8]]
I've modified arr in a few places.
arr.transpose.each_with_index.with_object({}) do |(col,j),h|
i = 0
h[j] = col.chunk(&:itself).each_with_object({}) do |(x,arr),g|
count = arr.size
g.update(i=>{ value: x, number: count }) if count > 1
i += count
end
end
#=> {0=>{},
# 1=>{0=>{:value=>15, :number=>3}},
# 2=>{},
# 3=>{},
# 4=>{0=>{:value=>23, :number=>2}, 2=>{:value=>16, :number=>2}},
# 5=>{1=>{:value=>12, :number=>2}}
# 6=>{2=>{:value=>13, :number=>2}},
# 7=>{2=>{:value=>15, :number=>2}},
# 8=>{0=>{:value=>19, :number=>2}, 2=>{:value=>15, :number=>2}},
# 9=>{2=>{:value=>12, :number=>2}},
# 10=>{2=>{:value=>14, :number=>2}},
# 11=>{2=>{:value=>14, :number=>2}},
# 12=>{2=>{:value=> 8, :number=>2}},
# 13=>{0=>{:value=> 8, :number=>4}}}
The keys of this hash are indices of columns of arr. The values are hashes that contain the locations and counts of all vertically-adjacent elements which appear at least twice. The columns at indices 0, 2 and 3, are the only ones that contains no vertically-adjacent duplicate values. The column at index 1 contains 3 15's beginning at row index 0; the column at index 4 contains 2 23's, beginning at row index 0 and 2 16's, beginning at row index 2.

matrix = [
[13, 15, 18, 23, 23, 11, 14, 19, 19, 5, 10, 10, 8, 8],
[ 8, 15, 19, 21, 21, 12, 16, 18, 18, 11, 13, 13, 6, 6],
[ 9, 15, 21, 23, 23, 7, 13, 15, 15, 12, 14, 14, 8, 8],
[ 2, 8, 14, 16, 16, 7, 13, 15, 15, 12, 14, 14, 8, 8]
]
equal_surround = matrix
.each_with_index.map do |v,i|
v.each_with_index.map do |k,j|
if (i-1>=0 && k == matrix[i-1][j])
k
elsif (i+1 < matrix.length && k == matrix[i+1][j])
k
else
nil
end
end
end
=> [
[nil, 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
[nil, 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
[nil, 15, nil, nil, nil, 7, 13, 15, 15, 12, 14, 14, 8, 8],
[nil, nil, nil, nil, nil, 7, 13, 15, 15, 12, 14, 14, 8, 8]
]

You didn't show any code, so I won't write any either.
I can tell you that array#transpose should make this problem much more manageable, though.
You'll just need to iterate on the rows (former columns) and look for any repeating number.
You can either do it FORTRAN style with a loop or with fancier Enumerable methods, like each_with_index, map or chunk.

test_array = [
[13, 15, 18, 23, 23, 11, 14, 19, 19, 5, 10, 10, 8, 8],
[8, 15, 19, 21, 21, 12, 16, 18, 18, 11, 13, 13, 6, 6],
[9, 15, 21, 23, 23, 7, 13, 15, 15, 12, 14, 14, 8, 8],
[2, 8, 14, 16, 16, 7, 13, 15, 15, 12, 14, 14, 8, 8]
]
final_res = Hash.new {|h,k| h[k] = Array.new }
test_array.each_cons(2).to_a.each_with_index do |(a,b),i|
final_match = Hash.new {|h,k| h[k] = Array.new }
res = a & b
res.each do |ele|
a_index = a.each_index.select{|i| a[i] == ele}
b_index = b.each_index.select{|i| b[i] == ele}
(a_index & b_index).size > 0 ? final_match[ele] << (a_index & b_index) : ''
end
final_match.each_value {|v| v.flatten!}
final_res[:"Match Values Between Array #{i+1} amd Array #{i+2}"] << final_match
end
final_res.each do |a|
puts a
end
OUTPUT:
Match Values Between Array 1 amd Array 2
{15=>[1]}
Match Values Between Array 2 amd Array 3
{15=>[1]}
Match Values Between Array 3 amd Array 4
{15=>[7, 8], 7=>[5], 13=>[6], 12=>[9], 14=>[10, 11], 8=>[12, 13]}

Related

Sort a numpy array using multiple index and different order

I have an array of size 300x5 and I am trying to sort the array in such a way that column with index 4 is my primary index and in ascending order, index 1 is secondary index and in descending order, index 3 is tertiary index and in ascending order.
I have tried this using following code,
idx = np.lexsort((arr[:,3],arr[:,1][::-1],arr[:,4]))
arr= arr[idx]
where arr --> array of size 300x5
On executing this the secondary index also gets sorted in ascending order instead of descending order. Can anyone help me with this
I think you want -arr[:,1] and not arr[:,1][::-1] as the secondary index.
>>> import numpy as np
>>> arr = np.random.randint(0, 21, (300, 5))
>>> arr
array([[ 0, 19, 6, 19, 17],
[16, 2, 14, 17, 0],
[ 8, 17, 3, 17, 12],
...,
[ 4, 18, 18, 3, 8],
[10, 15, 4, 12, 4],
[ 9, 16, 12, 0, 12]])
>>> idx = np.lexsort((arr[:,3],-arr[:,1],arr[:,4]))
>>> arr = arr[idx]
>>> arr
array([[11, 20, 11, 18, 0],
[11, 16, 12, 2, 0],
[ 9, 16, 4, 8, 0],
...,
[20, 4, 5, 11, 20],
[ 9, 4, 0, 19, 20],
[ 9, 2, 4, 10, 20]])

Insert data from hash into an array at set index in Ruby

I’m doing Ruby task, which is “You have an array of numbers. Your task is to sort ascending odd numbers but even numbers must be on their places. Zero isn't an odd number and you don't need to move it. If you have an empty array, you need to return it”.
Decided to split initial array: odd nums pushed into another array, sorted it and even numbers added to hash, where the key is num and its initial index is value. After that, trying to insert the even nums from hash to odd array, at initial indexes of even nums. So, the code looks like this:
def sort_array(source_array)
even_nums = Hash.new
odd_nums = []
return source_array if source_array.length == 0
source_array.each_with_index {|n, ind| even_nums[n] = ind if n.even?}
source_array.select{|n| odd_nums.push(n) if n.odd?}
odd_nums.sort!
even_nums.each do |k, v|
odd_nums.insert(v, k)
end
odd_nums
end
With small array like [5, 3, 2, 8, 1, 4, 11] it works as expected but if I pass something bigger like [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20, -67, -12, 28, -28, -37, -27, -62, -54, 93, -61, 50, 65, -63, -62, 77, -16, 49, -43, 26, -73, -27, 88, -88, -62, 84, 54, 25, 25, -2, -99, 69, -23, 47, -92, 7, -62, -62, -58, -30, -75, -31, 65, -63, 16, 64, -7, -22, -6, -82]
I’m getting nils at the end of the sorted array. Like this:
[-99, -64, 40, -79, -75, -73, 2, 14, -67, -63, -44, -63, 20, -61, -12, 28, -28, -43, -37, -31, -54, -27, -27, 50, -23, -23, -7, 5, -16, 7, 25, 26, 25, 29, 47, -88, 49, 53, 54, 65, 65, -2, 69, 77, 93, nil, -92, nil, nil, 88, -58, -30, nil, nil, nil, nil, 16, 64, nil, -22, -6, -82, 84, nil, -62]
Struggling to understand, why it isn’t working with bigger arrays?
There's a fairly easy way to do this if you think about it as two operations:
def sort_array(arr)
# Extract and sort the odd values
odd = arr.select(&:odd?).sort
# Merge the sorted odd values back in
arr.map do |v|
v.odd? ? odd.shift : v
end
end
Not much to it.
You had some of the right pieces, but I think you got stuck in the weeds when it started to get overly complicated.
That can be done as follows.
def sort_odds(arr)
odd_pos = arr.each_index.select { |i| arr[i].odd? }
odd_pos.zip(odd_pos.sort_by { |i| arr[i] }).
each_with_object(arr.dup) do |(old_pos,new_pos),a|
a[new_pos] = arr[old_pos]
end
end
sort_odds [5, 3, 2, 8, 1, 4, 11]
# o o e e o e o
#=> [1, 3, 2, 8, 5, 4, 11]
The steps are as follows.
arr = [5, 3, 2, 8, 1, 4, 11]
# o o e e o e o
odd_pos = arr.each_index.select { |i| arr[i].odd? }
#=> [0, 1, 4, 6]
new_pos = odd_pos.zip(odd_pos.sort_by { |i| arr[i] })
#=> [[0, 4], [1, 1], [4, 0], [6, 6]]
new_pos.each_with_object(arr.dup) do|(old_pos,new_pos),a|
a[new_pos] = arr[old_pos]
end
#=> [1, 3, 2, 8, 5, 4, 11]
I think this other answer is simpler and more elegant than mine, but this works too. Notably, this solution would allow you to validate the position of your even numbers (for example, in a spec) by looking up the indexes and values in evens. Unless you already know what the output array should look like, this may matter when it comes time to debug the interim results.
def odd_sorted array
odds = array.select { |e| e.odd? }.sort
evens = array.each_with_index.select { |e| e.first.even? }
arr2 = Array.new(array.size)
# put even numbers in at their previous index
evens.each do |e|
arr2.each_index { |i| arr2[i] = e[0] if e[1] == i }
end
arr2.each_with_index { |e, i| arr2[i] = odds.shift if e.nil? }
end
odd_sorted [5, 3, 2, 8, 1, 4, 11]
#=> [1, 3, 2, 8, 5, 4, 11]
odd_sorted [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20]
#=> [84, -64, 40, -79, -23, 88, 2, 14, 5, 29, -44, 53, 20]
The Array#map solution is definitely more elegant, but this is (in my personal opinion) more debuggable. Your mileage in that regard will certainly vary.

Easy way to do nd-array contraction using advanced indexing in Python

I know there must be an elegant way to do this using advanced indexing, I just can't figure it out.
Suppose I have the (2,3,4) array
x = array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
and the (4,) array y = array([1,0,1,1])
What is the most elegant way to obtain the (3,4) array that
z = np.zeros((3,4))
for ii in xrange(3):
for jj in xrange(4):
z[ii,jj] = x[y[jj],ii,jj]
produces?
In [490]: x[y,:,np.arange(4)]
Out[490]:
array([[12, 16, 20],
[ 1, 5, 9],
[14, 18, 22],
[15, 19, 23]])
We need to transpose this. With a mix of basic and advanced indexing, the slice dimension has been put last:
In [491]: x[y,:,np.arange(4)].T
Out[491]:
array([[12, 1, 14, 15],
[16, 5, 18, 19],
[20, 9, 22, 23]])
(that basic/advanced quirk is documented and discussed in some SO.)
or with advanced indexing all around:
In [492]: x[y,np.arange(3)[:,None],np.arange(4)]
Out[492]:
array([[12, 1, 14, 15],
[16, 5, 18, 19],
[20, 9, 22, 23]])

Translate VBA array into Python

USING IDLE/Python 3.5.1
May I first of all begin by saying I am a reasonably experienced programmer in VBA but am on day 2 of Python. I assure you I have conducted many searches on this question but the 30 or so documents I have read do not seem to explain my problem.
May I also please request that any answers given are properly formatted code for Python 3.5.1 rather than helpful pointers to other documentation or links?
The Problem
I am running a report and outputting results as I go. I need to store the results (presumably in an array) during this so that I can refer to them afterwards. The report (and the populating of the array) can be rerun multiple times so please bear that in mind if using concepts like 'append' when building the array. The array has dimensions [25,4] - a maximum of 25 records with four items in each.
Day X Y Z Total
1 2 3 4 9
2 3 4 5 12 ...
(Purists: The total needs to be recorded rather than calculated because of rounding.)
I could solve the problem myself if someone could translate this bit of code into Python (from VBA for illustration purposes). I do not want to import the arrays module unless it's the only way. Note: Variable l is a loop that makes the array get built twice to demonstrate that the array needs to be capable of rebuilding from scratch rather than being created just the once.
Sub sArray()
Dim a(25, 4)
For l = 1 To 2
For i = 1 To 25
For j = 1 To 4
a(i, j) = Int(100 * Rnd(1)) + 1
Debug.Print a(i, j);
Next j
Next i
Next l
End Sub
Thanks,
Tom
I am not sure I got your question correctly...
If you want to make an array (list i a better term in this case) of size [25,4] this is one way to go:
import random
a = [[int(100*random.random())+1 for j in range(4)] for i in range(25)]
>>> print a
[[74, 17, 36, 75],
[1, 79, 33, 90],
[58, 66, 47, 95],
[35, 40, 87, 38],
[43, 46, 34, 66],
[69, 34, 26, 49],
[56, 83, 44, 14],
[2, 44, 54, 97],
[50, 21, 39, 60],
[13, 94, 12, 48],
[36, 13, 2, 71],
[77, 44, 31, 11],
[56, 26, 30, 39],
[17, 13, 83, 84],
[54, 37, 34, 18],
[5, 54, 88, 100],
[22, 77, 70, 21],
[51, 88, 26, 97],
[69, 33, 86, 48],
[42, 66, 38, 78],
[71, 43, 96, 23],
[6, 46, 100, 29],
[32, 86, 15, 48],
[96, 84, 8, 56],
[29, 64, 69, 79]]
if you want to show that "the array needs to be capable of rebuilding from scratch rather than being created just the once" (why would you need this??)
for l in range(2):
a = [[int(100*random.random())+1 for j in range(4)] for i in range(25)]
Also, the way of generating random numbers is odd (I have translated you method). To get the same result in python, just use random.randint(1,100) to generate random integers from 1 (i think you do not want to have 0 there) to whatever number you like.
If I have correctly understood from your comments, this is what you want:
def report(g=25):
array = []
for _ in range(g):
x = random.randint(1,10)
y = random.randint(1,10)
z = random.randint(1,10)
total = x+y+x
row = [x,y,z,total]
print(row)
array.append(row)
return array
result = report()
#prints all the rows while computing
>>> result #stores the "array"
[8, 4, 3, 20]
[10, 7, 4, 27]
[2, 4, 5, 8]
[8, 5, 8, 21]
[9, 7, 2, 25]
[2, 2, 3, 6]
[5, 8, 6, 18]
[8, 6, 1, 22]
[7, 6, 4, 20]
[7, 2, 10, 16]
[6, 5, 9, 17]
[3, 8, 8, 14]
[9, 1, 9, 19]
[1, 7, 7, 9]
[6, 6, 2, 18]
[9, 10, 1, 28]
[4, 6, 2, 14]
[6, 1, 6, 13]
[4, 1, 3, 9]
[5, 3, 5, 13]
[7, 5, 2, 19]
[9, 5, 7, 23]
[2, 5, 8, 9]
[3, 10, 4, 16]
[5, 6, 5, 16]

Sum of ruby arrays in a loop

I want to find sum of arrays from 1 to 100.Each number is converted to array containing its own digits eg 97 will be [9,7].Here is what i have tried
(1..100).to_a.each do |i|
i.to_s.split("").map(&:to_i).inject(0) { |sum, number| sum + number }
end
But the sum of arrays are not displayed correctly!
For example;We first create an array of the digits and then return sum
[1]=1
[5]=5
[1, 0]=1
[1, 1]=2
[1, 2]=3
[1, 3]=4
[1, 4]=5
So sum of individual arrays is to be returned.
each will iterate the array, but not create a new array with the result of the block. If you want the results at the end, you need to use map:
result = (1..100).to_a.map do |i|
i.to_s.split("").map(&:to_i).inject(0) { |sum, number| sum + number }
end
result
#=> [1, 2, 3, 4, ...]
For summing an array you can also simply write
array.inject(:+)
# instead of
array.inject(0) { |sum, number| sum + number }
Also, the to_a is not necessary, you can directly call each and/or map on a range. So the simplified code would be
result = (1..100).map do |i|
i.to_s.split("").map(&:to_i).inject(:+)
end
result
#=> [1, 2, 3, 4, ...]
For your codes, you just need to change each method to map.
(1..100).to_a.map do |i|
i.to_s.split("").map(&:to_i).inject(0) { |sum, number| sum + number }
end
more simplicity way:
result = (1..100).to_a.map{ |e| e.to_s.split('').map(&:to_i).reduce(:+) }
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1]
if you want to get the sum of result, just do like this:
result.reduce(:+)
If I understand your question correctly you want the sum of these new arrays, right? This should solve your problem:
(1..100).map { |i| i.to_s.split("").map(&:to_i).inject(:+) }.inject(:+)
digits and sum are newer methods, simplifying this to
(1..100).sum{|i| i.digits.sum}
Another alternative for modern newer versions of Ruby:
(1..100).flat_map(&:digits).sum

Resources