Replace from one array values of another array - ruby

I have two arrays:
array1 = [3, 4, 4, 5, 6, 7, 8, 8]
array2 = [4, 5, 8, 8]
I want to remove those elements of array1, which are found in array2, but only in one instance. The resulting array, array3, must be like this:
array3 = [3, 4, 6, 7]
I tried:
array3 = array1 - array2
but the result was unsatisfactory:
array3 -> [3, 6, 7]

This may not be the most efficient way of doing what you want, but it works:
array1 = [3, 4, 4, 5, 6, 7, 8, 8]
array2 = [4, 5, 8, 8]
array2.each do |item|
index = array1.index item
array1.delete_at index if index
end

A non-imperative just to show other ways of doing things. Using Facets (just for convenience to get the histogram), I'd write this. O(n):
require 'facets'
array3 = array1.reduce([array2.frequency, []]) do |(h, output), x|
if h[x] && h[x] > 0
[h.update(x => h[x] - 1), output]
else
[h, output << x]
end
end[1]
#=> [3, 4, 6, 7]
To make the snippet purely functional you would use Hash#merge/Array#+ instead of Hash#update/Array:<<, but due to the nature of these data structures it would be terribly inefficient.

Related

Repeating a loop when it reaches the end

I am trying to conceptualize the iteration of two loops
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
While the numbers_array iterates, add_to_array iterates simultaneously adding both elements together at the same time. The caveat is once add_to_array reaches the end, it starts over adding its element to the next index in numbers_array. So at numbers_array[4] we would be adding add_to_array[0] then adding numbers_array[5] to add_to_array[1] and so on. This process would repeat until we reach the end of the numbers_array.
Any input would be greatly appreciated!
You are looking for Enumerable#zip and Enumerable#cycle:
numbers_array = [1,2,3,4,5,6,7,8,9,10]
#⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
add_to_array = [1,2,3,4]
#⇒ [1, 2, 3, 4]
numbers_array.zip(add_to_array.cycle)
#⇒ [[1, 1], [2, 2], [3, 3], [4, 4], [5, 1],
# [6, 2], [7, 3], [8, 4], [9, 1], [10, 2]]
Now do whatever you want with the array returned. E.g. to reduce the zipped result summing elements, map ro Enumerable#sum:
numbers_array.zip(add_to_array.cycle).map(&:sum)
#⇒ [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
It works by using the % operator to cycle through the indexes.
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
numbers_array.map.with_index do |n, i|
n + add_to_array[i % add_to_array.length]
end
A cool method that's similar, if you didn't want to start over at the next array, would be .zip
https://apidock.com/ruby/Array/zip
add_to_array.zip(*numbers_array.each_slice(add_to_array.size)).
map { |a| a.sum { |e| e.to_i } }
#=> [16, 20, 13, 16]
e.to_i is needed to convert nil values to zeros. See NilClass#to_i.
Another option:
numbers_array.map { |e| e + add_to_array.rotate!.last }
# => [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
Drawback: add_to_array is mutated by rotate!

ruby find the index of the next available number

I have a find_num method that returns the index of a specified number in an ordered array, e.g.
find_num(6, [1, 4, 6, 9, 13]) #=> 2
however my spec also requires that if the number is not available it finds the position of the next highest number so ...
find_num(8, [1, 4, 6, 9, 13]) #=> 3
as 9 is the next available number.
Having trouble implementing this... I have thought of iterating through the whole array but I am told to take into account the array could be large...
You can pass a block to index and it ...
[...] returns the index of the first object for which the block returns true. Returns nil if no match is found.
Examples:
[1, 4, 6, 9, 13].index { |n| n >= 6 } #=> 2
[1, 4, 6, 9, 13].index { |n| n >= 8 } #=> 3
[1, 4, 6, 9, 13].index { |n| n >= 15 } #=> nil
Because this requires the array to be ordered, you can also use bsearch_index which performs a binary search.
you can also find a index of any element in array like this.
2.1.8 :040 > [1, 4, 6, 9, 13].index(6)
=> 2
2.1.8 :041 > [1, 4, 6, 9, 13].index(15)
=> nil
def find_num(n,a)
a.each_with_index.to_a.sort_by(&:first).find { |nbr,_| nbr >= n }.last
end
find_num(6, [1, 4, 6, 9, 13])
#=> 2
find_num(8, [1, 4, 6, 9, 13]) #=> 3
#=> 3
The steps for
n = 8
a = [1, 4, 6, 9, 13]
are as follows.
b = a.each_with_index.to_a
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
c = b.sort_by(&:first)
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
d = c.find { |nbr,_| nbr >= n }
#=> [9, 3]
d.last
#=> 3

Ruby code to merge two arrays not working

nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
def mergeArrays (ar1, ar2)
result = (ar1 << ar2).flatten!
require 'pp'
pp %w(result)
end
As simple as this. I am trying to merge these two arrays and display the result. I am also brand-brand new to Ruby. This is the first function I am writing in this language. Trying to learn here. Also how can I remove the duplicates?
It would help if you give example inputs and outputs so we know exactly what you want. When you use the word "merge", I think you actually just want to add the arrays together:
ar1 = [1, 2, 3]
ar2 = [3, 4, 5]
ar3 = ar1 + ar2 # => [1, 2, 3, 3, 4, 5]
Now if you want to remove duplicates, use Array#uniq:
ar4 = ar3.uniq # => [1, 2, 3, 4, 5]
There is no need to write a method to do any of this since the Ruby Array class already supports it. You should skim through the documentation of the Array class to learn more things you can do with arrays.
What do you mean 'not working'?
Similar questions have been asked here:
Array Merge (Union)
You have two options: the pipe operator (a1 | a2) or concatenate-and-uniq ((a1 + a2).uniq).
Also be careful about using <<, this will modify the original variable, concatenating ar2 onto the end of the original ar1.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
result = (nums1<< nums2).flatten!
nums1
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
nums2
=> [5, 6, 7, 8, 9]
result
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
Additionally- just another Ruby tip, you do not need the destructive flatten! with ! versus the regular flatten. The regular flatten method will return a new Array, which you assign to result in your case. flatten! will flatten self in place, altering whatever Array it's called upon, rather than returning a new array.
You can merge Arrays using '+' operator and you can ignore the duplicated values using .uniq
>> nums1 = Array[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> nums2 = Array[5, 6, 7, 8, 9]
=> [5, 6, 7, 8, 9]
>> def mergeArrays (nums1, nums2)
>> result = (nums1 + nums2).uniq
>> end
=> :mergeArrays
>> mergeArrays(nums1,nums2)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
p nums1.concat(nums2).uniq

Ruby, perform operation on an array and return the new array, aswell as "changes"

I am looking for a way to perform a certain operation (for instance delete_if) on an array and return both the deleted elements, and the remaining elements.
For example
a = [1,2,3,4,5,6,7,8,9,10]
a.delete_if {|x| x.even? } #=> [[1, 3, 5, 7, 9]]
But what I am looking for is something like
a = [1,2,3,4,5,6,7,8,9,10]
a.some_operation #=> [[1,3,5,7,9],[2,4,6,8,10]]
How would I go about doing this?
Using Enumerable#partition:
a = [1,2,3,4,5,6,7,8,9,10]
a.partition &:even?
# => [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
The first element of the Enumerable#partition return value contains the elements that are evaluated to true in the block. So you need to use odd? to get what you want.
a.partition &:odd?
# => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]
You might be looking for something like this:
a = [1,2,3,4,5,6,7,8,9,10]
a.group_by { |x| x.even? }.values
#=> [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

How can I interweave items from two arrays?

How can I go from this:
for number in [1,2] do
puts 1+number
puts 2+number
puts 3+number
end
which will return 2,3,4 then 3,4,5 -> 2,3,4,3,4,5. This is just an example, and clearly not the real use.
Instead, I would like it to return 2,3 3,4 4,5 -> 2,3,3,4,4,5. I would like each of the puts to be iterated for each of the possible values of number; In this case 1 and 2 are the two possible values of 'number', before moving on to the next puts.
One way to do this is to create two lists, [2,3,4] and [3,4,5] and then use the zip method to combine them like [2,3,4].zip([3,4,5]) -> [2,3,3,4,4,5].
zip is good. You should also look at each_cons:
1.9.2p290 :006 > [2,3,4].each_cons(2).to_a
=> [[2, 3], [3, 4]]
1.9.2p290 :007 > [2,3,4,5,6].each_cons(2).to_a
=> [[2, 3], [3, 4], [4, 5], [5, 6]]
1.9.2p290 :008 > [2,3,4,5,6].each_cons(3).to_a
=> [[2, 3, 4], [3, 4, 5], [4, 5, 6]]
Because each_cons returns an Enumerator, you can use a block with it, as mentioned in the documentation for it, or convert it to an array using to_a like I did above. That returns the array of arrays, which can be flattened to get a single array:
[2,3,4,5].each_cons(2).to_a.flatten
=> [2, 3, 3, 4, 4, 5]
From the ri docs:
Iterates the given block for each array of consecutive elements. If no
block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) {|a| p a}
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
Maybe not the most readable code but you could use inject on the first range to create an array based on the summed up second range.
(1..3).inject([]){|m,n| (1..2).each{|i| m<<n+i }; m }
=> [2, 3, 3, 4, 4, 5]
This might be a little more readable
res=[]
(1..3).each{|r1| (1..2).each{|r2| res<<r1+r2 } }
[1, 2, 3].each { |i| [1, 2].each { |y| puts i + y } }

Resources