Find values in common between two arrays - ruby

If I want to compare two arrays and create an interpolated output string if an array variable from array y exists in x how can I get an output for each matching element?
This is what I was trying but not quite getting the result.
x = [1, 2, 4]
y = [5, 2, 4]
x.each do |num|
puts " The number #{num} is in the array" if x.include?(y.each)
end #=> [1, 2, 4]

You can use the set intersection method & for that:
x = [1, 2, 4]
y = [5, 2, 4]
x & y # => [2, 4]

x = [1, 2, 4]
y = [5, 2, 4]
intersection = (x & y)
num = intersection.length
puts "There are #{num} numbers common in both arrays. Numbers are #{intersection}"
Will output:
There are 2 numbers common in both arrays. Numbers are [2, 4]

OK, so the & operator appears to be the only thing you need to do to get this answer.
But before I knew that I wrote a quick monkey patch to the array class to do this:
class Array
def self.shared(a1, a2)
utf = a1 - a2 #utf stands for 'unique to first', i.e. unique to a1 set (not in a2)
a1 - utf
end
end
The & operator is the correct answer here though. More elegant.

Related

Ruby Second Smallest Number and index

Trying to use Ruby to find the second smallest number and the index from an input. I have the logic working from a hard coded array but can't seem to get the input from a user to work successfully. Thoughts?
print "Enter a list of numbers: "
nums = [gets]
#nums = [3,1,7,5]
lists = nums.sort
A = lists[1]
B = nums.find_index(A)
print "The second smallest number is: #{A} and the index is #{B}"
Suppose the user entered
str = " 2, -3 , 4, 1\n"
Then
arr = str.split(/ *, */).map(&:to_i)
#=> [2, -3, 4, 1]
The regular expression matches zero or more spaces followed by a comma followed by zero or more spaces.
The second smallest element, together with its index, can be obtained as follows.
n, i = arr.each_with_index.min(2).last
#=> [1, 3]
n #=> 1
i #=> 3
See Enumerable#min.
The steps are as follows.
enum = arr.each_with_index
#=> #<Enumerator: [2, -3, 4, 1]:each_with_index>
We can see the elements that will be generated by this enumerator by converting it to an array.
enum.to_a
#=> [[2, 0], [-3, 1], [4, 2], [1, 3]]
Continuing,
a = enum.min(2)
#=> [[-3, 1], [1, 3]]
min(2) returns the two smallest elements generated by enum. min compares each pair of elements with the method Array#<=>. (See especially the third paragraph of the doc.) For example,
[2, 0] <=> [-3, 1]
#=> 1
[4, 2] <=> [1, 3]
#=> 1
[1, 3] <=> [1, 4]
#=> -1
min would therefore order these pairs as follows.
[2, 0] > [-3, 1]
[4, 2] > [1, 3]
[1, 3] < [1, 4]
I've included the last example (though enum.to_a does not contain a second 1 at index 4) to illustrate that the second element of each two-element array serves as a tie-breaker.
As we want the second-smallest element of arr there is one final step.
n, i = a.last
#=> [1, 3]
n #=> 1
i #=> 3
Note that
n, i = [3, 2, 4, 2].each_with_index.min(2).last
#=> [2, 3]
n #=> 2
If we wanted n to equal 3 in this case we could write
n, i = [3, 2, 4, 2].uniq.each_with_index.min(2).last
#=> [3, 0]
n #=> 3
If the entries were floats or a mix of floats and integers we need only replace to_i with to_f.
str = "6.3, -1, 2.4, 3\n"
arr = str.split(/ *, */).map(&:to_f)
#=> [6.3, -1.0, 2.4, 3.0]
n, i = arr.each_with_index.min(2).last
#=> [2.4, 2]
n #=> 2.4
i #=> 2
Scan Input Strings and Map to Integers
The Kernel#gets method returns a String, so you have to parse and sanitize the user input to convert it to an Array. For example:
nums = gets.scan(/\d+/).map &:to_i
This uses String#scan to parse the input string, and Array#map to feed each element of the resulting array to String#to_i. The return value of this method chain will be an Array, which is then assigned to your nums variable.
Results of Example Data
Given input with inconsistent spacing or numbers of digits like:
1,2, 3, 4, 5, 10, 201
the method chain will nevertheless assign sensible values to nums. For example, the input above yields:
#=> [1, 2, 3, 4, 5, 10, 201]

How to return a splat from a method in Ruby

I wanted to create a method for array's to get a splat of the array in return. Is this possible to do in Ruby?
For example here's my current code:
Array.module_eval do
def to_args
return *self
end
end
I expect [1,2,3].to_args to return 1,2,3 but it ends up returning [1,2,3]
You cannot return a "splat" from Ruby. But you can return an array and then splat it yourself:
def args
[1, 2, 3]
end
x, y, z = args
# x == 1
# y == 2
# z == 3
x, *y = args
# x == 1
# y == [2, 3]
Of course, this works on any array, so really there is no need for monkey patching a to_args method into Array - it's all about how the calling concern is using the splat operator:
arr = [1, 2, 3]
x, y, z = arr
x, *y = arr
*x, y = arr
Same mechanism works with block arguments:
arr = [1, 2, 3]
arr.tap {|x, *y| y == [2, 3]}
Even more advanced usage:
arr = [1, [2, 3]]
x, (y, z) = arr
The concept that clarifies this for me is that although you can simulate the return of multiple values in Ruby, a method can really return only 1 object, so that simulation bundles up the multiple values in an Array.
The array is returned, and you can then deconstruct it, as you can any array.
def foo
[1, 2]
end
one, two = foo
Not exactly. What it looks like you're trying to do (the question doesn't give usage examples) is to force multiple return values. However, returning the splatted array self may do exactly what you need, as long as you're properly handling multiple return values on the calling side of the equation.
Consider these examples:
first, *rest = [1, 2, 3] # first = 1, rest = [2, 3]
*rest, last = [1, 2, 3] # rest = [1, 2], last = 3
first, *rest, last = [1, 2, 3] # first = 1, rest = [2], last = 3
Other than this, I can't actually see any way to capture or pass along multiple values like you're suggesting. I think the answer for your question, if I understand it correctly, is all in the caller's usage.

How to drop the end of an array in Ruby

Array#drop removes the first n elements of an array. What is a good way to remove the last m elements of an array? Alternately, what is a good way to keep the middle elements of an array (greater than n, less than m)?
This is exactly what Array#pop is for:
x = [1,2,3]
x.pop(2) # => [2,3]
x # => [1]
You can also use Array#slice method, e.g.:
[1,2,3,4,5,6].slice(1..4) # => [2, 3, 4, 5]
or
a = [1,2,3,4,5,6]
a.take 3 # => [1, 2, 3]
a.first 3 # => [1, 2, 3]
a.first a.size - 1 # to get rid of the last one
The most direct opposite of drop (drop the first n elements) would be take, which keeps the first n elements (there's also take_while which is analogous to drop_while).
Slice allows you to return a subset of the array either by specifying a range or an offset and a length. Array#[] behaves the same when passed a range as an argument or when passed 2 numbers
this will get rid of last n elements:
a = [1,2,3,4,5,6]
n = 4
p a[0, (a.size-n)]
#=> [1, 2]
n = 2
p a[0, (a.size-n)]
#=> [1, 2, 3, 4]
regard "middle" elements:
min, max = 2, 5
p a.select {|v| (min..max).include? v }
#=> [2, 3, 4, 5]
I wanted the return value to be the array without the dropped elements. I found a couple solutions here to be okay:
count = 2
[1, 2, 3, 4, 5].slice 0..-(count + 1) # => [1, 2, 3]
[1, 2, 3, 4, 5].tap { |a| a.pop count } # => [1, 2, 3]
But I found another solution to be more readable if the order of the array isn't important (in my case I was deleting files):
count = 2
[1, 2, 3, 4, 5].reverse.drop count # => [3, 2, 1]
You could tack another .reverse on there if you need to preserve order but I think I prefer the tap solution at that point.
You can achieve the same as Array#pop in a non destructive way, and without needing to know the lenght of the array:
a = [1, 2, 3, 4, 5, 6]
b = a[0..-2]
# => [1, 2, 3, 4, 5]
n = 3 # if we want drop the last n elements
c = a[0..-(n+1)]
# => [1, 2, 3]
Array#delete_at() is the simplest way to delete the last element of an array, as so
arr = [1,2,3,4,5,6]
arr.delete_at(-1)
p arr # => [1,2,3,4,5]
For deleting a segment, or segments, of an array use methods in the other answers.
You can also add some methods
class Array
# Using slice
def cut(n)
slice(0..-n-1)
end
# Using pop
def cut2(n)
dup.tap{|x| x.pop(n)}
end
# Using take
def cut3(n)
length - n >=0 ? take(length - n) : []
end
end
[1,2,3,4,5].cut(2)
=> [1, 2, 3]

Add two arrays into another array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ruby: sum corresponding members of two arrays
So I'm struggling to do this.
I am trying to add two arrays and put the results into a third one. Now I want to be able to do this through a function though.
So I have Array_One, Array_Two, and Array_Three.
I would want to call the "compare function" to one and two and make Three that length and then if they the lengths matched I would want to add One and Two and put the results in three.
Could this be done in one function?
Better way to do this?
That's my thought process but I don't have the knowledge of Ruby to do this.
EDIT:
Sorry for the vagueness.
Array_One = [3,4]
Array_Two = [1,3]
Array_Three= []
I would want to pass One and Two through a function that compares the length and verifies they're the same length.
Then I would, in my mind, send it through a function that actually does the adding.
So in the end I would have Array_Three = [4,7]
Hope that helps.
Your question's description is little confusing, but I think this may be what you want:
def add_array(a,b)
a.zip(b).map{|pair| pair.reduce(&:+) }
end
irb> add_array([1,2,3],[4,5,6])
=> [5, 7, 9]
In addition it generalize to add multiple arrays quite easily:
def add_arrays(first_ary, *other_arys)
first_ary.zip(*other_arys).map{|column| column.reduce(&:+) }
end
irb> add_arrays([1,2,3],[4,5,6])
=> [5, 7, 9]
irb> add_arrays([1,2,3],[4,5,6],[7,8,9])
=> [12, 15, 18]
One possible method (without any checks) assuming I understood your question:
x = [1, 2, 3]
y = [4, 5, 6]
z = []
x.each_with_index do |v, i|
z << v + y[i]
end
Another method (still assuming I understood the question):
[x, y].transpose.map {|v| v.reduce(:+)}
If by add you mean element-wise addition than you can use:
def add(a, b)
a.zip(b).map &:sum
end
assuming your environment has sum defined.
Sum is an alias for reduce(&:+), which you can use instead.
zip is a function that takes two arrays and returns an array of arrays:
[a1, a2, a3, ..., an].zip [b1, b2, b3, ..., bm]
#=> [[a1, b1], [a2, b2], [a3, b3], ..., [an, bn]]
# assuming n <= m
We than take our array of arrays and sum all numbers in that array together and than collect the results with map.
map is a function that takes a block and produces an array:
[c1, c2, c3, ..., cn].map &block
# => [block.call(c1), block.call(c2), block.call(c3), ..., block.call(cn)]
So if we get example input say
a = [1, 2, 3]
b = [4, 2, 5]
a.zip(b) #=> [[1,4], [2,2], [3,5]]
a.zip(b).map(&:sum) #=> [[1,4].sum, [2,2].sum, [3,5].sum] #=> [5, 4, 8]
Now we can check that the same length by using an if condition:
def add(a, b)
a.zip(b).map &:sum if a.size == b.size
end
If you want to add two arrays, you can simply add them:
array1 = [1, 2, 3]
array2 = ['a', 'b', 'c']
if array1.length == array2.length
array3 = array1 + array2
end
# array3 = [1, 2, 3, 'a', 'b', 'c']

How can I get the intersection, union, and subset of arrays in Ruby?

I want to create different methods for a class called Multiset.
I have all the required methods, but I'm unsure of how to write intersection, union, and subset methods.
For intersection and union, my code starts like this:
def intersect(var)
x = Multiset.new
end
Here is an example:
X = [1, 1, 2, 4]
Y = [1, 2, 2, 2]
then the intersection of X and Y is [1, 2].
I assume X and Y are arrays? If so, there's a very simple way to do this:
x = [1, 1, 2, 4]
y = [1, 2, 2, 2]
# intersection
x & y # => [1, 2]
# union
x | y # => [1, 2, 4]
# difference
x - y # => [4]
Source
Utilizing the fact that you can do set operations on arrays by doing &(intersection), -(difference), and |(union).
Obviously I didn't implement the MultiSet to spec, but this should get you started:
class MultiSet
attr_accessor :set
def initialize(set)
#set = set
end
# intersection
def &(other)
#set & other.set
end
# difference
def -(other)
#set - other.set
end
# union
def |(other)
#set | other.set
end
end
x = MultiSet.new([1,1,2,2,3,4,5,6])
y = MultiSet.new([1,3,5,6])
p x - y # [2,2,4]
p x & y # [1,3,5,6]
p x | y # [1,2,3,4,5,6]
If Multiset extends from the Array class
x = [1, 1, 2, 4, 7]
y = [1, 2, 2, 2]
z = [1, 1, 3, 7]
UNION
x.union(y) # => [1, 2, 4, 7] (ONLY IN RUBY 2.6)
x.union(y, z) # => [1, 2, 4, 7, 3] (ONLY IN RUBY 2.6)
x | y # => [1, 2, 4, 7]
DIFFERENCE
x.difference(y) # => [4, 7] (ONLY IN RUBY 2.6)
x.difference(y, z) # => [4] (ONLY IN RUBY 2.6)
x - y # => [4, 7]
INTERSECTION
x.intersection(y) # => [1, 2] (ONLY IN RUBY 2.7)
x & y # => [1, 2]
For more info about the new methods in Ruby 2.6, you can check this blog post about its new features

Resources