Ruby: Array contained in Array, any order [duplicate] - ruby

This question already has answers here:
Check if an array is subset of another array in Ruby
(4 answers)
Closed 6 years ago.
Suppose I have the following Ruby code:
array_1 = ['a', 'b']
array_2 = ['a', 'b', 'c']
some_function(array_1, array_2) # => True
some_function(array_2, array_1) # => False
some_function(['a', 'b'], ['a', 'd']) # => False
some_function(['x', 'y'], array_2) # => False
I am pretty much looking for some_function to return True when Parameter 2 contains all of the elements in Parameter 1.

def f a,b
(a-b).empty?
end

From a previous post,
def f a,b
(a-b).empty?
end
will not work the way you expect, for example:
a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a2 = [2, 3, 5, 9]
(a1-a2).empty? # returns true
however,
a1-a2 # returns [1, 4, 6, 7, 8], not empty
thus f returns false.
A more accurate solution, if you want a one-liner would be:
def f a,b
a&b == b
end
a&b will return all elements that are in both a and b then we check to see if that is equal to b
For ambiguity sake:
def f a,b
(a&b == a) || (a&b == b)
end

def f a,b
tmp = a.map(|i| b.include?(i))
tmp.include?(false)
end

Related

Find all max of elements of an array [duplicate]

This question already has answers here:
Returning all maximum or minimum values that can be multiple
(3 answers)
Closed 8 years ago.
Suppose I have a array, namely arr: [1, 2, 3, 4, 8, 8], and I want to find all max elements in this array:
arr.allmax # => [8, 8]
Is there a built-in method combinations to solve this? I don't like to monkey patch as I am doing now:
class Array
def allmax
max = self.max
self.select { |e| e == max }
end
end
Monkey patch is not a good idea, I could just do:
some_array.select { |e| e == some_array.max }
and it will work as allmax. Thanks for all answers and comments for inspirations.
Here's a fun way to do it.
arr.sort!.slice arr.index(arr[-1]) || 0..-1
Sort the array, then find the leftmost index of the array which matches the rightmost index of the array, and take the subslice that matches that range (or the range 0..-1 if the array is empty).
This one is kind of fun in that it requires no intermediate arrays, though it does mutate the input to achieve the one-liner.
Here is one way :
2.1.0 :006 > arr = [1, 2, 3, 4, 8, 8]
=> [1, 2, 3, 4, 8, 8]
2.1.0 :007 > arr.group_by { |i| i }.max.last
=> [8, 8]
2.1.0 :008 >
Here is a method :-
def all_max(arr)
return [] if arr.empty?
arr.group_by { |i| i }.max.last
end
Another way:
def all_max(arr)
return [] if arr.empty?
mx = arr.max
[mx] * arr.count { |e| e == mx }
end
all_max([1, 2, 3, 4, 8, 8])
#=> [8, 8]
To construct the array in a single pass, you could do this:
arr.each_with_object([]) do |e,a|
if a.empty?
a << e
else
case e <=> a.first
when 0 then a << e
when 1 then a.replace([e])
end
end
end

Ruby each and collect change array of arrays

I expected that Array.each and Array.collect would never change an object, like in this example:
a = [1, 2, 3]
a.each { |x| x = 5 }
a #output => [1, 2, 3]
But this doesn't seem to be the case when you are working with an array of arrays or an array of hashes:
a = [[1, 2, 3], [10, 20], ["a"]]
a.each { |x| x[0]=5 }
a #output => [[5, 2, 3], [5, 20], [5]]
Is this behaviour expected or am I doing something wrong?
Doesn't this make ruby behaviour a little unexpected? For example, in C++ if a function argument is declared const, one can be confident the function won't mess with it (ok, it can be mutable, but you got the point).
a = [[1, 2, 3], [10, 20], ["a"]]
a.each { |x| x[0]=5 }
In the above example, x is an array ( which you are passing to the block in each iteration ), from which you are accessing an element from its 0th index, and updating it. As array is mutable object, it also updating. Here a is an array of array.
In 1st iteration x is [1, 2, 3]. Now you are calling, Array#[]= method to update the 0th element of [1, 2, 3].
In 2nd iteration x is [10, 20]. same as above.
..and so on.. Thus after #each has completed its iterations, you got modified a.
a = [1, 2, 3]
a.each { |x| x = 5 }
In the above example, you are passing the array element to the each block, which are Fixnum object, and not mutable also. Here a ia an array of elements, and you are just accessing those elements.
update ( to clear OP's comment )
a = [[1, 2, 3], [10, 20], ["a"]]
a.each do |x|
# here x is holding the object from the source array `a`.
x # => [1, 2, 3]
x.object_id # => 72635790
# here you assgined a new array object, which has the same content as the
# inner array element [1, 2, 3]. But strictly these are 2 different object. Check
# out the object_id of those two.
x = [1, 2, 3]
x # => [1, 2, 3]
x.object_id # => 72635250
break # used break to stop iteration after 1st one.
end
Using each or map does not change the array itself. But is might look like it changes elements in the array. In fact when a array is holding references to other object, that references are keep unchanged, but the referenced object itself can change. I agree it is surprising when you learn it.
What you noticed:
a = ['a', 'b', 'c']
a.each { |x| x[0] = 'x' }
puts a # => ['x', 'x', 'x']
Here the first array element still references the same string, but the string has change.
Why it is important to understand this references?
array = ['a', 'b', 'c']
a = array
b = array
puts b # => ['a', 'b', 'c']
a[0] = 'x'
puts b # => ['x', 'b', 'c']
Does freeze protect us from changes?
a = ['a', 'b', 'c'].freeze
a << ['d'] # throws 'can't modify frozen Array (RuntimeError)'
Seems so. But again only for the array itself. It does not deep freeze the array.
a[0][0] = 'x'
puts a.inspect ['x', 'b', 'c']
I suggest the read about topics like referenced objects, pointers, call by value vs. call by reference.

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 do I check an array for duplicates? [duplicate]

This question already has answers here:
How to find and return a duplicate value in array
(23 answers)
Closed 8 years ago.
I've got an array A. I'd like to check if it contains duplicate values. How would I do so?
Just call uniq on it (which returns a new array without duplicates) and see whether the uniqed array has less elements than the original:
if a.uniq.length == a.length
puts "a does not contain duplicates"
else
puts "a does contain duplicates"
end
Note that the objects in the array need to respond to hash and eql? in a meaningful for uniq to work properly.
In order to find the duplicated elements, I use this approach (with Ruby 1.9.3):
array = [1, 2, 1, 3, 5, 4, 5, 5]
=> [1, 2, 1, 3, 5, 4, 5, 5]
dup = array.select{|element| array.count(element) > 1 }
=> [1, 1, 5, 5, 5]
dup.uniq
=> [1, 5]
If you want to return the duplicates, you can do this:
dups = [1,1,1,2,2,3].group_by{|e| e}.keep_if{|_, e| e.length > 1}
# => {1=>[1, 1, 1], 2=>[2, 2]}
If you want just the values:
dups.keys
# => [1, 2]
If you want the number of duplicates:
dups.map{|k, v| {k => v.length}}
# => [{1=>3}, {2=>2}]
Might want to monkeypatch Array if using this more than once:
class Array
def uniq?
self.length == self.uniq.length
end
end
Then:
irb(main):018:0> [1,2].uniq?
=> true
irb(main):019:0> [2,2].uniq?
=> false

How can I check if a Ruby array includes one of several values?

I have two Ruby arrays, and I need to see if they have any values in common. I could just loop through each of the values in one array and do include?() on the other, but I'm sure there's a better way. What is it? (The arrays both hold strings.)
Thanks.
Set intersect them:
a1 & a2
Here's an example:
> a1 = [ 'foo', 'bar' ]
> a2 = [ 'bar', 'baz' ]
> a1 & a2
=> ["bar"]
> !(a1 & a2).empty? # Returns true if there are any elements in common
=> true
Any value in common ? you can use the intersection operator : &
[ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
If you are looking for a full intersection however (with duplicates) the problem is more complex there is already a stack overflow here : How to return a Ruby array intersection with duplicate elements? (problem with bigrams in Dice Coefficient)
Or a quick snippet which defines "real_intersection" and validates the following test
class ArrayIntersectionTests < Test::Unit::TestCase
def test_real_array_intersection
assert_equal [2], [2, 2, 2, 3, 7, 13, 49] & [2, 2, 2, 5, 11, 107]
assert_equal [2, 2, 2], [2, 2, 2, 3, 7, 13, 49].real_intersection([2, 2, 2, 5, 11, 107])
assert_equal ['a', 'c'], ['a', 'b', 'a', 'c'] & ['a', 'c', 'a', 'd']
assert_equal ['a', 'a', 'c'], ['a', 'b', 'a', 'c'].real_intersection(['a', 'c', 'a', 'd'])
end
end
Using intersection looks nice, but it is inefficient. I would use "any?" on the first array (so that iteration stops when one of the elements is found in the second array). Also, using a Set on the second array will make membership checks fast. i.e.:
a = [:a, :b, :c, :d]
b = Set.new([:c, :d, :e, :f])
c = [:a, :b, :g, :h]
# Do a and b have at least a common value?
a.any? {|item| b.include? item}
# true
# Do c and b have at least a common value?
c.any? {|item| b.include? item}
#false
Array#intersect? (Ruby 3.1+)
Starting from Ruby 3.1, there is a new Array#intersect? method,
which checks whether two arrays have at least one element in common.
Here is an example:
a = [1, 2, 3]
b = [3, 4, 5]
c = [7, 8, 9]
# 3 is the common element
a.intersect?(b)
# => true
# No common elements
a.intersect?(c)
# => false
Also, Array#intersect? can be much faster than alternatives since it avoids creating an intermediate array, returns true as soon as it finds a common element, it is implemented in C.
Sources:
Ruby 3.1 adds Array#intersect?.
Pull Request.
Discussion.
Source code.
Try this
a1 = [ 'foo', 'bar' ]
a2 = [ 'bar', 'baz' ]
a1-a2 != a1
true

Resources