Checking if an element of an array is included in another array - arrays

I have arrays:
a = [1,3,4,5]
b = [1,2,3]
Is there any short way to check as follows?
a.include? b
It should return true as 3 is there.
We can do:
b.each do |bb|
puts true if a.include? bb
end
but this is not a good way to iterate over a big array. Or:
c = [2,4]
a.include? b
should return true without iteration.

You could intersect the arrays. If the intersection is non-empty, the arrays have common elements:
a = [1,2,3,4]
b = [2,4]
(a & b).any? # true
!(a & b).empty? # => true
This is quite efficient as it uses a temporary hash under the hood.

I Hope you may be want something like intersect kind of thing used in Set
require 'set'
Set[1,3,4,5].intersect? Set[1,2,3] # => true
Here it is

If I subtract b - a and a has some elements that b has too, then the new array of b - a has a smaller number of elements, because b - a returns all elements, which b has but a has not. I can check the result against the original size.
a = [1,3,4,5]
b = [1,2,3]
b.size > (b - a).size
# => true

You can use array intersection:
a = [1,2,3,4]
b = [2,4]
c = [5,6]
It gives following results:
(a & b).any?
# true
(a & c).any?
# false

Related

Finding the common value in N number of arrays

I have n number of arrays and I want to work out if there is a common value in these arrays. If I knew the number of arrays I could do something like:
a = [1,2,3]
b = [2,4,5]
c = [2,6,7]
x = a & b & c
x == [2]
However, this isn't possible if you don't know the number of arrays. So far I've come up with this:
array_of_integers = [[1,2,3],[2,4,5]....]
values = []
array_of_integers.each_with_index do |array, index|
values = if index.zero?
array
else
values & array
end
end
# `values` will be an array of common values
However, this doesn't seem very efficient. Is there a better way?
However, this isn't possible if you don't know the number of arrays.
Actually, Enumerable#reduce can help with it:
[[1,2,3], [2,4,5], [2,6,7]].reduce(&:&) # => [2]
&:& looks interesting, but it's just:
[[1,2,3], [2,4,5], [2,6,7]].reduce { |memo, el| memo & el } # => [2]
Or it's also possible to do it as #Jagdeep suggested:
[[1,2,3], [2,4,5], [2,6,7]].reduce(:&) # => [2]

create two arrays based on what meet a condition and the diff

Lets suppose I've the follow array:
a = [1,2,3]
I want to split it in two arrays from it, one with items for which a condition is true, and other for which the same condition is false:
b, c = a.split_in_two_arrays_or_something_like_that {|x| x == 3}
#=> b = [3]
#=> c = [1,2]
How can I do that in ruby? I don't want to repeat code with something like:
b = a.reject {|x| x == 3}
c = a.reject {|x| x != 3}
Nor iterate over the array twice.
Is there some method that return me something different than the modified array? For example, delete_if will work if it would return the deleted elements, but the original array would've keeped the same, but it doesn't work that way.
Use Enumerable#partition to separate the elements in your array according to a condition. We define the condition in partition's block:
a = [1,2,3]
b, c = a.partition { |x| x == 3 } #=> [[3], [1, 2]]
b #=> [3]
c #=> [1, 2]
This method creates an array with two subarrays.
The first subarray contains the values for which partition's block returns true.
The second subarray contains the values for which partition's block returns false.
Finally we apply parallel-assignment to assign variables b to the first subarray and c to the second.

Difference Between Arrays Preserving Duplicate Elements in Ruby

I'm quite new to Ruby, and was hoping to get the difference between two arrays.
I am aware of the usual method:
a = [...]
b = [...]
difference = (a-b)+(b-a)
But the problem is that this is computing the set difference, because in ruby, the statement (a-b) defines the set compliment of a, relative to b.
This means [1,2,2,3,4,5,5,5,5] - [5] = [1,2,2,3,4], because it takes out all of occurrences of 5 in the first set, not just one, behaving like a filter on the data.
I want it to remove differences only once, so for example, the difference of [1,2,2,3,4,5,5,5,5], and [5] should be [1,2,2,3,4,5,5,5], removing just one 5.
I could do this iteratively:
a = [...]
b = [...]
complimentAbyB = a.dup
complimentBbyA = b.dup
b.each do |bValue|
complimentAbyB.delete_at(complimentAbyB.index(bValue) || complimentAbyB.length)
end
a.each do |aValue|
complimentBbyA.delete_at(complimentBbyA.index(aValue) || complimentBbyA.length)
end
difference = complimentAbyB + complimentBbyA
But this seems awfully verbose and inefficient. I have to imagine there is a more elegant solution than this. So my question is basically, what is the most elegant way of finding the difference of two arrays, where if one array has more occurrences of a single element then the other, they will not all be removed?
I recently proposed that such a method, Ruby#difference, be added to Ruby's core. For your example, it would be written:
a = [1,2,2,3,4,5,5,5,5]
b = [5]
a.difference b
#=> [1,2,2,3,4,5,5,5]
The example I've often given is:
a = [3,1,2,3,4,3,2,2,4]
b = [2,3,4,4,3,4]
a.difference b
#=> [1, 3, 2, 2]
I first suggested this method in my answer here. There you will find an explanation and links to other SO questions where I proposed use of the method.
As shown at the links, the method could be written as follows:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
.....
ha = a.group_by(&:itself).map{|k, v| [k, v.length]}.to_h
hb = b.group_by(&:itself).map{|k, v| [k, v.length]}.to_h
ha.merge(hb){|_, va, vb| (va - vb).abs}.inject([]){|a, (k, v)| a + [k] * v}
ha and hb are hashes with the element in the original array as the key and the number of occurrences as the value. The following merge puts them together and creates a hash whose value is the difference of the number of occurrences in the two arrays. inject converts that to an array that has each element repeated by the number given in the hash.
Another way:
ha = a.group_by(&:itself)
hb = b.group_by(&:itself)
ha.merge(hb){|k, va, vb| [k] * (va.length - vb.length).abs}.values.flatten

Creating an array from variables without nil values in Ruby

What's the most idiomatic way to create an array from several variables without nil values?
Given these variables:
a = 1
b = nil
c = 3
I would like to create an array ary:
ary #=> [1, 3]
I could use Array#compact:
ary = [a, b, c].compact
ary #=> [1, 3]
But putting everything in an array just to remove certain elements afterwards doesn't feel right.
Using if statements on the other hand produces more code:
ary = []
ary << a if a
ary << b if b
ary << c if c
ary #=> [1, 3]
Is there another or a preferred way or are there any advantages or drawbacks using either of the above?
PS: false doesn't necessarily have to be considered. The variables are either truthy (numbers / strings / arrays / hashes) or nil.
If you are concerned about performance, best way would be probably to use destructive #compact! to avoid allocating memory for second array.
I was hoping for a way to somehow "skip" the nil values during array creation. But after thinking about this for a while, I realized that this can't be achieved because of Ruby's way to handle multiple values. There's no concept of a "list" of values, multiple values are always represented as an array.
If you assign multiple values, Ruby creates an array:
ary = 1, nil, 3
#=> [1, nil, 3]
Same for a method taking a variable number of arguments:
def foo(*args)
args
end
foo(1, nil, 3)
#=> [1, nil, 3]
So even if I would patch Array with a class method new_without_nil, I would end up with:
def Array.new_without_nil(*values)
values.compact!
values
end
This just moves the code elsewhere.
Everything is an object
From an OO point of view, there's nothing special about nil - it's an object like any other. Therefore, removing nil's is not different from removing 1's.
Using a bunch of if statements on the other hand is something I'm trying to avoid when writing object oriented code. I prefer sending messages to objects.
Regarding "advantages or drawbacks":
[...] with compact / compact!
creates full array and shrinks it as needed
short code, often fits in one line
is easily recognized
evaluates each item once
faster (compiled C code)
[...] with << and if statements
creates empty array and grows it as needed
long code, one line per item
purpose might not be as obvious
items can easily be commented / uncommented
evaluates each item twice
slower (interpreted Ruby code)
Verdict:
I'll use compact, might have been obvious.
Here is a solution that uses a hash.
With these values put in an array:
a = 1; b = nil; c = 3; d = nil; e = 10;
ary = [a, b, c, d, e]
There are two nil items in the result which would require a compact to remove both "nil" items.
However the same variables added to a hash:
a = 1; b = nil; c = 3; d = nil; e = 10;
hash = {a => nil, b => nil, c => nil, d => nil, e => nil}
There is just one "nil" item in the result which can easily be removed by hash.delete(nil).

Counting matching elements in an array

Given two arrays of equal size, how can I find the number of matching elements disregarding the position?
For example:
[0,0,5] and [0,5,5] would return a match of 2 since there is one 0 and one 5 in common;
[1,0,0,3] and [0,0,1,4] would return a match of 3 since there are two matches of 0 and one match of 1;
[1,2,2,3] and [1,2,3,4] would return a match of 3.
I tried a number of ideas, but they all tend to get rather gnarly and convoluted. I'm guessing there is some nice Ruby idiom, or perhaps a regex that would be an elegant answer to this solution.
You can accomplish it with count:
a.count{|e| index = b.index(e) and b.delete_at index }
Demonstration
or with inject:
a.inject(0){|count, e| count + ((index = b.index(e) and b.delete_at index) ? 1 : 0)}
Demonstration
or with select and length (or it's alias – size):
a.select{|e| (index = b.index(e) and b.delete_at index)}.size
Demonstration
Results:
a, b = [0,0,5], [0,5,5] output: => 2;
a, b = [1,2,2,3], [1,2,3,4] output: => 3;
a, b = [1,0,0,3], [0,0,1,4] output => 3.
(arr1 & arr2).map { |i| [arr1.count(i), arr2.count(i)].min }.inject(0, &:+)
Here (arr1 & arr2) return list of uniq values that both arrays contain, arr.count(i) counts the number of items i in the array.
Another use for the mighty (and much needed) Array#difference, which I defined in my answer here. This method is similar to Array#-. The difference between the two methods is illustrated in the following example:
a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1, 3, 2, 2]
For the present application:
def number_matches(a,b)
left_in_b = b
a.reduce(0) do |t,e|
if left_in_b.include?(e)
left_in_b = left_in_b.difference [e]
t+1
else
t
end
end
end
number_matches [0,0,5], [0,5,5] #=> 2
number_matches [1,0,0,3], [0,0,1,4] #=> 3
number_matches [1,0,0,3], [0,0,1,4] #=> 3
Using the multiset gem:
(Multiset.new(a) & Multiset.new(b)).size
Multiset is like Set, but allows duplicate values. & is the "set intersection" operator (return all things that are in both sets).
I don't think this is an ideal answer, because it's a bit complex, but...
def count(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end
def matches(a1, a2)
m = 0
a1_counts = count(a1)
a2_counts = count(a2)
a1_counts.each do |e, c|
m += [a1_counts, a2_counts].min
end
m
end
Basically, first write a method that creates a hash from an array of the number of times each element appears. Then, use those to sum up the smallest number of times each element appears in both arrays.

Resources