Ruby Logical Operators - Elements in one but not both arrays - ruby

Let's say I have two arrays:
a = [1,2,3]
b = [1,2]
I want a logical operation to perform on both of these arrays that returns the elements that are not in both arrays (i.e. 3). Thanks!

Arrays in Ruby very conveniently overload some math and bitwise operators.
Elements that are in a, but not in b
a - b # [3]
Elements that are both in a and b
a & b # [1, 2]
Elements that are in a or b
a | b # [1, 2, 3]
Sum of arrays (concatenation)
a + b # [1, 2, 3, 1, 2]
You get the idea.

p (a | b) - (a & b) #=> [3]
Or use sets
require 'set'
a.to_set ^ b

There is a third way of looking at this solution, which directly answers the question and does not require the use of sets:
r = (a-b) | (b-a)
(a-b) will give you what is in array a but not b:
a-b
=> [3]
(b-a) will give you what is in array b but not a:
b-a
=> []
OR-ing the two array subtractions will give you final result of anything that is not in both arrays:
r = ab | ba
=> [3]
Another example might make this even more clear:
a = [1,2,3]
=> [1, 2, 3]
b = [2,3,4]
=> [2, 3, 4]
a-b
=> [1]
b-a
=> [4]
r = (a-b) | (b-a)
=> [1, 4]

Related

How to delete a element from two arrays independently which has been copied from one another?

a = [1,2,3]
=> [1, 2, 3]
b = a
=> [1, 2, 3]
b.delete(1)
=> 1
b
=> [2, 3]
a
=> [2, 3]
Array A has given [1,2,3] values, and Array A has been copied to Array B
Then whenever I delete a element from Array B , the element gets deleted from Array A too
eg : If i delete element 1 from array B ,it gets deleted from array A too..
How to avoid this, How to delete an element from these arrays separately ?
You can use dup to create a copy of the array.
a = [1,2,3]
=> [1, 2, 3]
b = a.dup
=> [1, 2, 3]
a.delete(1)
=> 1
a
=> [2, 3]
b
=> [1, 2, 3]
EDIT:
As to why this is, when you assign b = a, you assign b to be a reference to a. This means that both variables refer the same underlying object. With dup we are forcing Ruby to create a copy of a.
You are creating a shallow copy in b, so the contents aren't copied. To copy them, use Object::clone: b = a.clone.

What does a comma followed by an equals sign mean in Ruby?

Just saw something like this in some Ruby code:
def getis;gets.split.map(&:to_i);end
k,=getis # What is this line doing?
di=Array::new(k){Array::new(k)}
It assigns the array's first element using Ruby's multiple assignment:
a, = [1, 2, 3]
a #=> 1
Or:
a, b = [1, 2, 3]
a #=> 1
b #=> 2
You can use * to fetch the remaining elements:
a, *b = [1, 2, 3]
a #=> 1
b #=> [2, 3]
Or:
*a, b = [1, 2, 3]
a #=> [1, 2]
b #=> 3
It works like this. If lhs has single element and rhs has multiple values then lhs gets assigned an array of values, like this.
a = 1,2,3 #=> a = [1,2,3]
Whereas if lhs has more elements than rhs, then excess elements in lhs are discarded
a,b,c = 1,2 #=> a = 1, b = 2, c = nil
Therefore
a, = 1,2,3 #=> a = 1. The rest i.e. [2,3] are discarded

How to find which array element is not present in another

Here is the scenario
a = [1,2,3]
b = [2,5,6]
I would like to find out which elements of b are not present in a. I could use include? to check which ones are. But I am looking for something entirely opposite here.
> a - b
=> [1, 3]
> b - a
=> [5, 6]
(I can't quite tell which way you want to go.)
a = [1,2,3]
b = [2,5,6]
b - a #=> [5, 6]

Compare two arrays in Ruby

I'd like to know whether two Ruby arrays have the same elements, though not necessarily in the same order. Is there a native way to do this? The equality operators for Array seem to check whether the items are the same and the order is the same, and I need to relax the latter condition.
This would be extremely easy to write, I just wonder if there's a native idiom.
If you don't have duplicate items either, you could use Set instead of Array:
Set implements a collection of unordered values with no duplicates.
This is a hybrid of Array's intuitive inter-operation facilities and
Hash's fast lookup.
Example:
require 'set'
s1 = Set.new [1, 2, 3] # -> #<Set: {1, 2, 3}>
s2 = [3, 2, 1].to_set # -> #<Set: {3, 2, 1}>
s1 == s2 # -> true
[2,1].uniq.sort == [1,2].uniq.sort #=> true
[2,1,4].uniq.sort == [1,2].uniq.sort #=> false
or
a1 = [1,2,3]
a2 = [2,3,1]
p (a2-a1).empty? && (a1-a2).empty? #=> true
a1 = [1,2,3]
a2 = [4,3,1]
p (a2-a1).empty? && (a1-a2).empty? #=> false
a1 = [1,2,3]
a2 = [2,3,1,5]
p (a2-a1).empty? && (a1-a2).empty? #=> false
This would be extremely easy to write, I just wonder if there's a native idiom.
I'm afraid there's no native idiom for it.
If your arrays contains multiple values that you want to count on both arrays you'll have to use #sort to put them in the same order. Once you have done that you can easily compare them:
a.sort == b.sort
Otherwise you can use #uniq that will extract the unique values of the arrays (to make it faster) and use #sort like above:
a.uniq.sort == b.uniq.sort
a1 = [1, 2, 3, 4]
a2 = [4, 2, 1, 3]
(a1 & a2).size == a1.size # => true
a3 = [1, 2, 3, 5]
(a1 & a3).size == a1.size # => false

How to do sane "set-difference" in Ruby?

Demo (I expect result [3]):
[1,2] - [1,2,3] => [] # Hmm
[1,2,3] - [1,2] => [3] # I see
a = [1,2].to_set => #<Set: {1, 2}>
b = [1,2,3].to_set => #<Set: {1, 2, 3}>
a - b => #<Set: {}> WTF!
And:
[1,2,9] - [1,2,3] => [9] # Hmm. Would like [[9],[3]]
How is one to perform a real set difference regardless of order of the inputs?
Ps. As an aside, I need to do this for two 2000-element arrays. Usually, array #1 will have fewer elements than array #2, but this is not guaranteed.
The - operator applied to two arrays a and b gives the relative complement of b in a (items that are in a but not in b).
What you are looking for is the symmetric difference of two sets (the union of both relative complements between the two). This will do the trick:
a = [1, 2, 9]
b = [1, 2, 3]
a - b | b - a # => [3, 9]
If you are operating on Set objects, you may use the overloaded ^ operator:
c = Set[1, 2, 9]
d = Set[1, 2, 3]
c ^ d # => #<Set: {3, 9}>
For extra fun, you could also find the relative complement of the intersection in the union of the two sets:
( a | b ) - ( a & b ) # => #<Set: {3, 9}>

Resources