how do i reverse individual (and specific) columns in a 2d array (RUBY) - ruby

RUBY the goal is to get the max value from each of the four zones and get their sum.
UPDATE I came up with a solution, I'm sorry about the mixup. It turned out that the matrix is a 2n x 2n matrix so it could have been greater or smaller than 4x4 in fact it. The solution i wrote below worked on all of the test cases. Here is a link to the problem
I tried doing matrix.transpose then I tried reversing the specific array, that didn't work for all edge cases.
Here is the code for that
def flippingMatrix(matrix)
2.times do
matrix = matrix.transpose
matrix = matrix.map do |array|
if (array[-1] == array.max) || (array[-2] == array.max)
array.reverse
else
array
end
end
end
return matrix[0][0] + matrix[0][1] + matrix[1][0] + matrix[1][1]
end
I gave up and tried the below, which in my mind works, it also works for most edge cases but not all.
But i'm getting an error (undefined method `[]' for nil:NilClass (NoMethodError))
keep in mind when I print the results or spot_1, spot_2, spot_3, or spot_4 I get the correct answer. does anyone have an idea why this is happening?
Here is a matrix that FAILED
[
[517, 37, 380, 3727],
[3049, 1181, 2690, 1587],
[3227, 3500, 2665, 383],
[4041, 2013, 384, 965]
]
**expected output: 12,881 (this fails)**
**because 4041 + 2013 + 3227 + 3500 = 12,881**
Here is a matrix that PASSED
[
[112, 42, 83, 119],
[56, 125, 56, 49],
[15, 78, 101, 43],
[62, 98, 114, 108],
]
**expected output: 414 (this passes)**
Here is the code
def flippingMatrix(matrix)
# Write your code here
spot_1 = [matrix[0][0] , matrix[0][3] , matrix[3][0] , matrix[3][3]].max
spot_2 = [matrix[0][1] , matrix[0][2] , matrix[3][1] , matrix[3][2]].max
spot_3 = [matrix[1][0] , matrix[1][3] , matrix[2][0] , matrix[2][3]].max
spot_4 = [matrix[1][1] , matrix[1][2] , matrix[2][1] , matrix[2][2]].max
return (spot_1 + spot_2 + spot_3 + spot_4)
end

I will answer your question and at the same time suggest two other ways to obtain the desired sum.
Suppose
arr = [
[ 1, 30, 40, 2],
[300, 4000, 1000, 200],
[400, 3000, 2000, 100],
[ 4, 10, 20, 3]
]
First solution
We see that the desired return value is 4444. This corresponds to
A B B A
C D D C
C D D C
A B B A
First create three helper methods.
Compute the largest value among the four inner elements
def mx(arr)
[arr[1][1], arr[1][2], arr[2][1], arr[2][2]].max
end
mx(arr)
#=> 4000
This is the largest of the "D" values.
Reverse the first two and last two rows
def row_flip(arr)
[arr[1], arr[0], arr[3], arr[2]]
end
row_flip(arr)
#=> [[300, 4000, 1000, 200],
# [ 1, 30, 40, 2],
# [ 4, 10, 20, 3],
# [400, 3000, 2000, 100]]
This allows us to use the method mx to obtain the largest of the "B" values.
Reverse the first two and last two columns
def column_flip(arr)
row_flip(arr.transpose).transpose
end
column_flip(arr)
#= [[ 30, 1, 2, 40],
# [4000, 300, 200, 1000],
# [3000, 400, 100, 2000],
# [ 10, 4, 3, 20]]
This allows us to use the method mx to obtain the largest of the "C" values.
Lastly, the maximum of the "A" values can be computed as follows.
t = row_flip(column_flip(arr))
#=> [[4000, 300, 200, 1000],
# [ 30, 1, 2, 40],
# [ 10, 4, 3, 20],
# [3000, 400, 100, 2000]]
mx(column_flip(t))
#=> 4
The sum of the maximum values may therefore be computed as follows.
def sum_max(arr)
t = column_flip(arr)
mx(arr) + mx(row_flip(arr)) + mx(t) + mx(row_flip(t))
end
sum_max(arr)
#=> 4444
Second solution
Another way is the following:
[0, 1].product([0, 1]).sum do |i, j|
[arr[i][j], arr[i][-j-1], arr[-i-1][j], arr[-i-1][-j-1]].max
end
#=> 4444
To see how this works let me break this into two statements add a puts statement. Note that, for each of the groups A, B, C and D, the block variables i and j are the row and column indices of the top-left element of the group.
top_left_indices = [0, 1].product([0, 1])
#=> [[0, 0], [0, 1], [1, 0], [1, 1]]
top_left_indices.sum do |i, j|
a = [arr[i][j], arr[i][-j-1], arr[-i-1][j], arr[-i-1][-j-1]]
puts a
a.max
end
#=> 4444
The prints the following.
[1, 2, 4, 3]
[30, 40, 10, 20]
[300, 200, 400, 100]
[4000, 1000, 3000, 2000]

ahhh I came up with an answer that answers all edge cases. I originally saw something like this in Javascript and kind of turned it into Ruby. Apparently some of the hidden edge cases (that were hidden) weren't all 4 by 4 some were smaller and some larger, that was the cause of the nil error.
Here is the solution:
def flippingMatrix(matrix)
total = []
(0...(matrix.length/2)).each do |idx1|
(0...(matrix.length/2)).each do |idx2|
total << [matrix[idx1][idx2],
matrix[(matrix.length - 1)-idx1][idx2],
matrix[idx1][(matrix.length - 1)-idx2],
matrix[(matrix.length - 1)-idx1][(matrix.length - 1)-idx2]].max
end
end
total.sum
end
Thank you all for your support! I hope this helps someone in the near future.

Related

How to add data to array and apply increase Qty for that?

hi i have an array that dimension after mining push to that how can i increase Qty after each pushing for similar dimension?
suppose to we have this array:
[x,y,Qty]
b=[]
b.push[100,50,1]
b.push[20,30,1]
b.push[100,50,1]
b.push[10,60,1]
how can i have this result: b = [ [100,50,2],[20,30,1],[10,60,1] ]
Suppose we are given the following (which differs slightly from the example).
b = [[100, 50, 1], [20, 30, 2], [100, 50, 1], [10, 60, 1]]
Let's begin with a relatively inefficient solution and then see how it can be improved.
Staightforward but relatively inefficient solution
We first compute the following array.
arr = b.map { |x,y,z| [x, y] }
#=> [[100, 50], [20, 30], [100, 50], [10, 60]]
Before continuing suppose b were to change in future such that each element contain four, rather than three, values, such as [100, 50, 20, 2]. We could change the calculation of arr to arr = b.map { |w,x,y,z| [w, x, y] }, but a better solution is to use the splat operator, *, to perform Array#decomposition. By using the splat operator we need not change this code if the size of the elements of b changes.
arr = b.map { |*a,z| a }
#=> [[100, 50], [20, 30], [100, 50], [10, 60]]
For each element (array) e of b, the array a will contain all but the last element of e and z will contain the last element of e, regardless of the size of e.
Next we use the method Array#uniq to obtain the unique elements of arr.
arr_uniq = arr.uniq
#=> [[100, 50], [20, 30], [10, 60]]
We may then compute the desired array as follows.
arr_uniq.map do |a|
tot = 0
b.each { |*c,z| tot += z if a == c }
[*a, tot]
end
#=> [[100, 50, 2], [20, 30, 2], [10, 60, 1]]
Recall that tot += z is effectively the same as tot = tot + z.
This approach is relatively inefficient because the array b is traversed completely for each element of arr_uniq. We should be able to make only a single pass through b.
Improving efficiency
We step through each element of b to construct a hash.
h = {}
b.each do |*a,z|
if h.key?(a)
h[a] += z
else
h[a] = z
end
end
h #=> {[100, 50]=>2, [20, 30]=>2, [10, 60]=>1}
We now need only convert h to the desired array (though it may be more useful to stop here and use the hash for subsequent calculations).
h.map { |k,v| [*k, v] }
#=> [[100, 50, 2], [20, 30, 2], [10, 60, 1]]
We now consider how to make this calculation more Ruby-like.
Wrapping up
To improve upon the construction of h above we make use of two methods: the form of class method Hash::new that takes an argument called the default value; and Enumerable#each_with_object.
b.each_with_object(Hash.new(0)) { |(*a,z),h| h[a] += z }
.map { |k,v| [*k, v] }
#=> [[100, 50, 2], [20, 30, 2], [10, 60, 1]]
This first step is to compute a hash:
b.each_with_object(Hash.new(0)) { |(*a,z),h| h[a] += z }
#=> {[100, 50]=>2, [20, 30]=>2, [10, 60]=>1}
You may wish to review the link to array decomposition that I gave earler to see how the block variables a, z and h in |(*a,z),h| are assigned values.
If a hash is defined
h = Hash.new(0)
h[k] returns the default value, here zero, if h does not contain a key k.
Initially,
h[[100, 50]] += 1
expands to
h[[100, 50]] = h[[100, 50]] + 1
Since h has no keys at this time--specifically no key [100, 50]--h[[100, 50]] on the right returns the default value, so the expression becomes
h[[100, 50]] = 0 + 1
h #=> { [100, 50] => 1 }
Later, when the same key [100, 50] is encountered (when h #=> { [100, 50] => 1, [20, 30] => 2 }), the expresson becomes
h[[100, 50]] = 1 + 1
h #=> { [100, 50] => 2, [20, 30] => 2 }
This time, h has a key [100, 50], so h[[100, 50]] on the right returns its value, which equals 1 (and the default value is not used).
Alternative method: use Enumerable#group_by
We could alternatively compute the desired array as follows.
b.group_by { |*a,z| a }
.map { |k,v| [*k, v.sum(&:last)] }
#=> [[100, 50, 2], [20, 30, 2], [10, 60, 1]]
The first step is to compute a hash:
b.group_by { |*a,z| a }
#=> {[100, 50]=>[[100, 50, 1], [100, 50, 1]],
[20, 30]=>[[20, 30, 2]], [10, 60]=>[[10, 60, 1]]}
When compared to the method above that employs Hash::new, this calculation has the minor disadvantage that the memory requirements for the values in the intermediate are somewhat greater.

Codewars exercise "Count by X" not working

I'm tryng to do this exercise:
Create a function with two arguments that will return a list of length
(n) with multiples of (x).
Assume both the given number and the number of times to count will be
positive numbers greater than 0.
Return the results as an array (or list in Python, Haskell or Elixir).
Examples:
count_by(1,10) #should return [1,2,3,4,5,6,7,8,9,10]
count_by(2,5) #should return [2,4,6,8,10]
Quite easy, nothing to say. BUT I really do not understand why my code is not working.
PLease DO NOT GIVE ME NEW CODE OR SOLUTION, I JUST WANT TO UNDERSTAND WHY MINE DOESN'T WORK.
My solution:
def count_by(x, n)
arrays = []
arrays.push(x)
valore_x = x
unless arrays.count == n
arrays.push( x + valore_x)
x += valore_x
end
return arrays
end
count_by(3, 5)
ERROR MESSAGE =
Expected: [1, 2, 3, 4, 5], instead got: [1, 2]
✘ Expected: [2, 4, 6, 8, 10], instead got: [2, 4]
✘ Expected: [3, 6, 9, 12, 15], instead got: [3, 6]
✘ Expected: [50, 100, 150, 200, 250], instead got: [50, 100]
✘ Expected: [100, 200, 300, 400, 500], instead got: [100, 200].
So looks like that my code do not put all the numbers. Thanks.
Now you have answer on your question, so I just recommend one more variant of solution, I think it's more ruby-way :)
def count_by(x, y)
y.times.with_object([]) do |i, result|
result << x*(i+1)
end
end
Change
unless arrays.count == n
to
until arrays.count == n
unless is not a loop. It's just like an if, but the code is executed if the condition is false.
until is just like while, but the code is executed while the condition is false.
Array::new can be used here.
def count_by(x, n)
Array.new(n) { |i| x*(i+1) }
end
count_by(1,5) #=> [1, 2, 3, 4, 5]
count_by(2,5) #=> [2, 4, 6, 8, 10]
count_by(3,5) #=> [3, 6, 9, 12, 15]
count_by(50,5) #=> [50, 100, 150, 200, 250]
count_by(100,5) #=> [100, 200, 300, 400, 500]
count_by(0,5) #=> [0, 0, 0, 0, 0]
Just a few other ways to achieve the same result. Using Numeric#step:
x.step(by: x, to: x*y).entries
shorter but less readable:
x.step(x*y, x).entries
or using a range with Range#step:
(x..x*y).step(x).entries
In each of the examples entries can be replaced with to_a

How to find number of a specific integer in an array of Fixnums ruby

I'm doing an exercise now where I'm looking for all of the zeros in an array.
The input is:
numbers = [1, 3, 500, 200, 4000, 3000, 10000, 90, 20, 500000]
I want to sort them into a hash by the number of zeros. The expected output is:
expected = {0=>[1, 3], 2=>[500, 200], 3=>[4000, 3000], 4=>[10000], 1=>[90, 20], 5=>[500000]}
I have the structure built but I'm not sure how to count the number of zeros:
grouped = Hash.new {|hash, key| hash[key] = []}
numbers.each do |num|
grouped[num] << num
end
EDITED for clarity:
Any advice would be appreciated. Also, a lot of the advice I read on this recommended converting the array of integers to a string in order to solve the problem. Is there a way to count the number of digits (not just zeros) without converting the array to a string? The expected output in this case would look like:
expected = {1=>[1, 3], 2=>[90, 20], 3=>[500, 200], 4=>[4000, 3000], 5=>[10000], 6=>[500000]
Thanks in advance.
Like many transformations you'll want to do, this one's found in Enumerable.
Grouping by number of digits:
grouped = numbers.group_by { |n| Math.log10(n).to_i + 1 }
# => {1=>[1, 3], 3=>[500, 200], 4=>[4000, 3000], 5=>[10000], 2=>[90, 20], 6=>[500000]}
Grouping by number of zeroes:
grouped = numbers.group_by { |n| n.to_s.match(/0+$/) ? $&.length : 0 }
# => {0=>[1, 3], 2=>[500, 200], 3=>[4000, 3000], 4=>[10000], 1=>[90, 20], 5=>[500000]}
The group_by method is a handy way to convert an Array to a Hash with things organized into pigeon-holes.
I wound up using
grouped = Hash.new {|hash, key| hash[key] = []}
numbers.each do |num|
grouped[num.to_s.count('0')] << num
end
but I really liked the variation in responses. I didn't realize there were so many ways to go about this. Thank you everyone.
If you wish to group non-negative integers by the number of zero digits they contain, you can do this:
def nbr_zeroes(n)
return 1 if n == 0
m = n
i = 0
while m > 0
i += 1 if m % 10 == 0
m /= 10
end
i
end
numbers = [1, 3, 500, 200, 4000, 3000, 10000, 90, 20, 500000]
numbers.group_by { |i| nbr_zeroes(i) }
#=> { 0=>[1, 3], 2=>[500, 200], 3=>[4000, 3000], 4=>[10000] }
numbers = [100000, 100001, 304070, 3500040, 314073, 2000, 314873, 0]
numbers.group_by { |i| nbr_zeroes(i) }
#=> { 5=>[100000], 4=>[100001, 3500040], 3=>[304070, 2000],
# 1=>[314073, 0], 0=>[314873] }
Group by floor of log base 10?
1.9.3-p484 :014 > numbers.each {|n| grouped[Math.log10(n).floor] << n}
=> [1, 3, 500, 200, 4000, 3000, 10000, 90, 20, 500000]
1.9.3-p484 :016 > grouped
=> {0=>[1, 3], 2=>[500, 200], 3=>[4000, 3000], 4=>[10000], 1=>[90, 20], 5=>[500000]}
Or try 1 + Math.log10(n).floor if you need the keys to be the actual number of digits.

Using formula and counter in an array

What I have is:
array.map!{
|c| c*(y**(z-1))
z=z+1
}
array contains
[10, 10, 10]
The output isn't what I want, it is
[2, 3, 4]
I want the z to function as a counter, it was defined earlier as 1, and the y was defined earlier. also, as 16 or 36 (depending on user input)
So if I input the same array with 3 10s. I want array to be (when the y is 16):
[10, 160, 2560]
There are more idiomatic ways to achieve this in ruby, for instance:
y = 16
array = Array.new(3) { |i| 10*(y**i) }
# => [10, 160, 2560]
Or alternatively, if the contents are not always the constant 10, there is this:
y = 16
array = [10, 10, 10]
array.map!.with_index { |c, i| c*(y**i) }
# => [10, 160, 2560]
The above two examples leave the indexing to the looping construct, which is good, because it's one less thing for you to worry about.
Write it as
z = 1; y =16
array = [10, 10, 10]
array.map { |c| z=z+1 ; c*(y**(z-2)) }
# => [10, 160, 2560]
With Array#map, block returned the last expression for each iteration. In your case it was z = z + 1. Your initial z was 1. So in your first z = z+1 evaluated and it was 2, next iteration it incremented again by 1, so value of z is 3, same way in the last pass z becomes 4. So finally you got the new array as [2, 3, 4].
One Rubyish way :
y = 16
3.times.map{|i| 10*y**i} # => [10, 160, 2560]

Finding continuous number sequence

How to find the longest continuous number sequence in array of number arrays? Each array of numbers represent one or zero numbers in resulting sequence.
Example ([] - represents array (like in javascript)):
[
[1, 5, 6],
[7],
[22, 34],
[500, 550],
[60, 1],
[90, 100],
[243],
[250, 110],
[150],
[155],
[160]
]
Correct output would be: [1, 7, 22, 60, 90, 110, 150, 155, 160]
Detailed output:
1, -- index 1 all 1, 5 and 6 would match here, pick the smallest
7, -- index 2
22, -- index 3
-- index 4 skipped, the sequence would end here or wouldn't be the longest possible
60, -- index 5 picked 60, because 1 wouldn't continue in the sequence
90, -- index 6
-- index 7 skipped, the sequence would end here or wouldn't be the longest possible
110, -- index 8
150, -- index 9
155, -- index 10
160 -- index 11
A possible approach is to use dynamic programming using as parameters the last value and the index of first sub-array to consider.
This is a solution in Python based on recursion with memoization
data = [[1, 5, 6],
[7],
[22, 34],
[500, 550],
[60, 1],
[90, 100],
[243],
[250, 110],
[150],
[155],
[160]]
def longest(x0, i, _cache=dict()):
if i == len(data):
return []
try:
return _cache[x0, i]
except KeyError:
best = longest(x0, i+1)
for x in data[i]:
if x >= x0:
L = [x] + longest(x, i+1)
if len(L) > len(best):
best = L
_cache[x0, i] = best
return best
print longest(0, 0)

Resources