Merge two ordered arrays into one ordered array - arrays

I am writing a method that takes two sorted arrays and I want it to return a merged array with all the values sorted. Given the two arrays below:
array_one = [3, 4, 8]
array_two = [1, 5, 7]
I want my merge_arrays method to return:
[1, 3, 4, 5, 7, 8]
My current algorithm is below:
def merge_arrays(array_one, array_two)
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0..merged_array_size - 1)
if array_one[current_index_on_one] < array_two[current_index_on_two]
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
return merged_array
end
I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this. I debugged the variables in the conditionals and they are giving true or false values. I'm not sure what is causing this error.

Maybe I am missing the point but you can do:
(array_one + array_two).sort
=> [1, 3, 4, 5, 7, 8]

I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this.
You start by comparing index 0 to index 0:
[3, 4, 8] [1, 5, 7]
0-----------0 #=> 3 < 1
Then you increment the lower value's index by 1:
[3, 4, 8] [1, 5, 7]
0--------------1 #=> 3 < 5
And so on:
[3, 4, 8] [1, 5, 7]
1-----------1 #=> 4 < 5
[3, 4, 8] [1, 5, 7]
2--------1 #=> 8 < 5
[3, 4, 8] [1, 5, 7]
2-----------2 #=> 8 < 7
At that point you get:
[3, 4, 8] [1, 5, 7]
2--------------3 #=> 8 < nil
Index 3 is outside the array's bounds, so array_two[current_index_on_two] returns nil and:
if array_one[current_index_on_one] < array_two[current_index_on_two]
# ...
end
becomes
if 8 < nil
# ...
end
resulting in ArgumentError(comparison of Integer with nil failed). If nil is on the left hand side, you'd get NoMethodError (undefined method `<' for nil:NilClass).

Here's one way you can write merge using recursion. Note, as you specified, both inputs must already be sorted otherwise the output will be invalid. The inputs can vary in size.
def merge (xs, ys)
if xs.empty?
ys
elsif ys.empty?
xs
else
x, *_xs = xs
y, *_ys = ys
if x < y
[x] + (merge _xs, ys)
else
[y] + (merge xs, _ys)
end
end
end
merge [ 1, 3, 4, 6, 8, 9 ], [ 0, 2, 5, 7 ]
# => [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Assuming you have two sorted arrays. You need to create pipeline using recursion going to crunch through each array. checking at each iteration to see
which value at index 0 of either array is lower, removing that from the array and appending that value to the result array.
def merge_arrays(a, b)
# build a holder array that is the size of both input arrays O(n) space
result = []
# get lower head value
if a[0] < b[0]
result << a.shift
else
result << b.shift
end
# check to see if either array is empty
if a.length == 0
return result + b
elsif b.length == 0
return result + a
else
return result + merge_arrays(a, b)
end
end
> a = [3, 4, 6, 10, 11, 15]
> b = [1, 5, 8, 12, 14, 19]
> merge_arrays(a, b)
#=> [1, 3, 4, 5, 6, 8, 10, 11, 12, 14, 15, 19]

I made slight changes to your code in order to make it work. See the comments inside.
array_one = [2, 3, 4, 8, 10, 11, 12, 13, 15]
array_two = [1, 5, 6, 7, 9, 14]
def merge_arrays(array_one, array_two)
array_one, array_two = array_two, array_one if array_one.length > array_two.length # (1) swap arrays to make satement (3) work, need array_two always be the longest
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0...merged_array_size-1) # (2) three points to avoid the error
if (!array_one[current_index_on_one].nil? && array_one[current_index_on_one] < array_two[current_index_on_two]) # (3) check also if array_one is nil
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
merged_array[current_merged_index] = array_one[current_index_on_one] || array_two[current_index_on_two] # (4) add the missing element at the end of the loop, looks what happen if you comment out this line
return merged_array
end
p merge_arrays(array_one, array_two)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
The error was coming because the loop was making one step over. The solution is to stop before and insert the missing element at the end of the loop.
It works also with:
# for i in (1...merged_array_size)
# and
# for i in (1..merged_array_size-1)
# and
# (merged_array_size-1).times do

arr1 = [3, 4, 8, 9, 12]
arr2 = [1, 5, 7, 8, 13]
arr = [arr1, arr2]
idx = [0, 0]
(arr1.size + arr2.size).times.with_object([]) do |_,a|
imin = [0, 1].min_by { |i| arr[i][idx[i]] || Float::INFINITY }
a << arr[imin][idx[imin]]
idx[imin] += 1
end
#=> [1, 3, 4, 5, 7, 8, 8, 9, 12, 13]

Related

Remove elements from an array into another array (Ruby)

I'm new here and new to coding and I can use some help with a problem I'm trying to solve.
I'm trying to remove all integers that are less than 5 from array
a = [1, 2, 3, 4, 5, 6] and put them into a new array b = [], and then print out the b array.
I've done many Google searches but I can't find anything that helps.
I'm starting to think this is not possible.
Please help!
Thanks
a = [1, 2, 3, 4, 5, 6]
b, a = a.partition { |i| i < 5 }
#=> [[1, 2, 3, 4], [5, 6]]
b #=> [1, 2, 3, 4]
a #=> [5, 6]
See Enumerable#partition.
a = [1, 2, 3, 4, 5, 6]
b = a.select { |i| i < 5 } # [1, 2, 3, 4]
a = a - b # [5, 6]
To actually remove elements from a while putting them in an existing array b, you could use reject!:
a = [1, 2, 3, 4, 5, 6]
b = []
a.reject! { |i| b << i if i < 5 }
a #=> [5, 6]
b #=> [1, 2, 3, 4]
If i < 5 evaluates to true, b << i puts that element in b and returns a truthy result which causes reject! to remove it from a.
Likewise, if i < 5 evaluates to false, b << i is skipped, the block returns a falsy result and that element remains in a.

Ruby - How do you perform an operation on each item of two (maybe more) arrays and populate them in a new array?

a = [6, 7, 8, 9, 10]
b = [1, 2, 3, 4, 5]
each of array a's items are divided by each of array b's items and put into a new array called c.
c = [6, 3, 2, 2, 2]
a = [6, 7, 8, 9, 10]
b = [1, 2, 3, 4, 5]
c = a.zip(b).map { |e| e.reduce :/ }
#⇒ [
# [0] 6,
# [1] 3,
# [2] 2,
# [3] 2,
# [4] 2
# ]
Array#zip zips the arrays together and then each element (array of 2 items zipped) is reduced with Integer#/.
I like mudasobwa's zip/map solution, but here are a couple alternatives:
a = [6, 7, 8, 9, 10]
b = [1, 2, 3, 4, 5]
c = Array.new(a.size) { |i| a[i] / b[i] }
c = a.map.with_index { |x, i| x / b[i] }
In particular, I might prefer the Array.new solution if the arrays aren't guaranteed to be the same length, because you can easily ensure you don't go over bounds:
c = Array.new([a.size, b.size].min) { |i| a[i] / b[i] }

How do I remove the beginning elements of my array only if the first element satisfies a condition?

In Ruby, let's say I have an array of ordreed, unique numbers
[0, 1, 2, 4, 6, 8, 10]
If the first element of the array is zero, how do I remove all the elements from teh beginning of the array that are consecutive, starting wiht zero? That is, in the above example, I would want to remove "0", "1", and "2" leaving me with
[4, 6, 8, 10]
But if my array is
[1, 2, 3, 10, 15]
I would expect the array to be unchanged because the first element is not zero.
You could use a mix of drop_while and with_index to only remove the first matching elements:
[0, 1, 2, 4, 6, 8, 10].drop_while.with_index{|x, i| x == i}
# [4, 6, 8, 10]
[1, 1, 2, 4, 6, 8, 10].drop_while.with_index{|x, i| x == i}
# [1, 1, 2, 4, 6, 8, 10]
Note that the second and third elements don't get deleted in the second example, even though they're equal to their indices.
Drop elements, as long as they are equal to their index:
a=a.drop_while.with_index{|e,i| e==i}
Sounds like you're trying to delete entities if they match their idx (provided the first idx is 0). Try this:
if array.first == 0
new_array = array.reject.each_with_index{ |item, idx| item == idx }
end
Although this will only work with ordered arrays of unique numbers, if you're not sure that they are then include: array = array.sort.uniq
You could do:
x = -1
while my_array.first == x + 1 do
x = my_array.shift
end
Note that array.shift is the same as array.pop except that it works from the start of the array.
If I understand you right, then it can be one of possible solutions:
def foo(array)
if array.first.zero?
array.keep_if.with_index { |e, ind| e != ind }
else
array
end
end
> foo([0, 1, 2, 5, 6, 7])
#=> => [5, 6, 7]
> foo([1, 2, 3])
#=> [1, 2, 3]
In short form:
a[0] == 0 ? a[3..-1] : a
In longer form:
if a.first == 0
a[3..(a.size)]
else
a
end

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.

Search for zero in 2D array and make a corresponding row and col 0

This is my code, which works, but it's too big. I want to refactor it.
req_row = -1
req_col = -1
a.each_with_index do |row, index|
row.each_with_index do |col, i|
if col == 0
req_row = index
req_col = i
break
end
end
end
if req_col > -1 and req_row > -1
a.each_with_index do |row,index|
row.each_with_index do |col, i|
print (req_row == index or i == req_col) ? 0 : col
print " "
end
puts "\r"
end
end
Input: 2D Array
1 2 3 4
5 6 7 8
9 10 0 11
12 13 14 15
Required output:
1 2 0 4
5 6 0 8
0 0 0 0
12 13 0 15
I'm surprised the Matrix class is not used more:
a = [[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 0, 11],
[12, 13, 14, 15]]
require 'matrix'
m = Matrix.rows(a)
#=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]
r, c = m.index(0)
#=> [2, 2]
Matrix.build(m.row_count, m.column_count) {|i,j| (i==r || j==c) ? 0 : m[i,j]}.to_a
#=> [[ 1, 2, 0, 4],
# [ 5, 6, 0, 8],
# [ 0, 0, 0, 0],
# [12, 13, 0, 15]]
Note Matrix objects are immutable. To change individual elements you must create a new matrix.
A slight modification is required if you wish to do this for every zero in the matrix:
a = [[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 0, 11],
[ 0, 13, 14, 15]]
require 'set'
m = Matrix.rows(a)
#=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [0, 13, 14, 15]]
zero_rows = Set.new
zero_columns = Set.new
m.each_with_index { |e,i,j| (zero_rows << i; zero_columns << j) if e.zero? }
zero_rows
#=> #<Set: {2, 3}>
zero_columns
#=> #<Set: {2, 0}>
Matrix.build(m.row_count, m.column_count) do |i,j|
(zero_rows.include?(i) || zero_columns.include?(j)) ? 0 : m[i,j]
end.to_a
#=> [[0, 2, 0, 4],
# [0, 6, 0, 8],
# [0, 0, 0, 0],
# [0, 0, 0, 0]]
Try this code:
req_row = req_col = -1
a.each_with_index do |row, index|
req_col = row.index(0) # searching index having value 0.
if req_col
req_row = index
break
end
end
a.each_with_index do |row,index|
row.each_with_index do |col, i|
print ((req_row == index or i == req_col) ? 0 : col).to_s + " "
end
puts "\r"
end
Based on the title of your question, here's solution that searches for positions of the zero values (fixate), then actually zeros out the appropriate row and column (clear, more aligned with the contents of your question):
def fixate matrix, search=0, replace=0
rcs = []
matrix.each_with_index do |row,r|
row.each_with_index do |col,c|
rcs << [ r, c ] if col == search
end
end
rcs.each do |(row, col)|
clear matrix, row, col, replace
end
matrix
end
def clear matrix, row, col, val=0
matrix[row].map! { |_| val } # Clear rows
matrix.each { |r| r[col] = val } # Clear columns
matrix
end
Quick test:
fixate [ # [
[ 1, 2, 3, 4 ], # [ 1, 2, 0, 4 ],
[ 5, 6, 7, 8 ], # [ 5, 6, 0, 8 ],
[ 9, 10, 0, 11 ], # [ 0, 0, 0, 0 ],
[ 12, 13, 14, 15 ] # [ 12, 13, 0, 15 ]
] # ]
Here's what I came up with:
zero_rows=[]
a.map do |col|
col.each_with_index do |el, index|
zero_rows.push(index) if el==0
end
col.fill(0) if col.include?(0)
end
a.map{|col| zero_rows.each{|index| col[index]=0} }
First, use map to iterate through the columns and fill them with zeros if they contain at least one 0. but, while doing so, add the indexes which contain a 0 to the zero_rows array.
Afterwards, iterate through the array once more and set the indexes of each column that match an index in zero_rows to 0.
You may know the map method as collect. They do the same thing.
Side Note:
If an array contains multiple zero's this code will zero out every applicable column. OP's example and some other answers here will only zero out the first column in which a 0 is found. If this is the behavior you are expecting, then see #Doguita's answer.
I don't know if this code is better than the other answers. I will test it later:
ary = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]
col = nil
row = ary.index{ |a| col = a.index(0) }
ary.each_with_index { |a, i| i == row ? a.fill(0) : a[col] = 0 } if col
p ary
# => [[1, 2, 0, 4], [5, 6, 0, 8], [0, 0, 0, 0], [12, 13, 0, 15]]
Obs: This answer assumes there's only one 0 to search in the array

Resources