Rails - Finding Intersections Between Multiple Arrays

rails - Finding intersections between multiple arrays

Use the & method of Array which is for set intersection.

For example:

> [1,2,3] & [2,3,4] & [0,2,6]
=> [2]

How do I find intersection of 3 arrays, while ignoring empty arrays?

One way to do it would be this:

[ a, b, c ].tap{ |a| a.delete( [] ) }.reduce( :& ) || []

Other options suggested in the discussion are:

[ a, b, c ].reject( &:empty? ).reduce( :& ) || []

and:

[ a, b, c ].select( &:any? ).reduce( :& ) || []

But in this last case, beware of non-empty arrays with explicit nil elements, such as [ nil ], cause they still fail #any? test.

Find intersection of arrays

Your arrays each contain a single element which is the string "1,2,3,4,5". You probably want an array with 5 elements instead ([1,2,3,4,5]). You can do so by splitting the array on a comma:

@array1 = params[:a].split(',')
@array2 = params[:b].split(',')

@intersection = @array1 & @array2
#=> ["1", "2", "3", "4", "5"]

In Rails, how do I perform an intersect of two arrays based on a field in each object in the arrays?

You can build an intersection of the values to find the common values, then select records that have the common values.

field_in_both = arr1.map(&:myfield1) & arr2.map(&:myfield1)
intersection = arr1.select{|obj| field_in_both.include? obj.myfield1} +
arr2.select{|obj| field_in_both.include? obj.myfield1}

I notice in your code, you're only storing records from arr1... if that's correct behaviour then you can simplify my answer

field_in_both = arr1.map(&:myfield1) & arr2.map(&:myfield1)
intersection = arr1.select{|obj| field_in_both.include? obj.myfield1}

Ruby - array intersection (with duplicates)

(array1 & array2).flat_map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [2,2,2,3,4,8]

The steps:

a = array1 & array2 
#=> [2, 3, 4, 8]

The first element of a (2) is passed to the block and assigned to the block variable:

n = 2

and the block calculation is performed:

[2]*[array1.count(2), array2.count(2)].min
#=> [2]*[4,3].min
#=> [2]*3
#=> [2,2,2]

so 2 is mapped to [2,2,2]. The calculations are similar for the remaining three elements of a. As I am using flat_map, this returns [2,2,2,3,4,8].

Do you have trouble remembering how Enumerable#flat_map differs from Enumerable#map? Suppose I had used map rather than flat_map. Then

a.map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [[2, 2, 2], [3], [4], [8]]

flat_map does nothing more that put a splat in front of each of those arrays:

[*[2, 2, 2], *[3], *[4], *[8]]
#=> [2, 2, 2, 3, 4, 8]

If the arrays array1 and array2 are large and efficiency is a concern, we could do a bit of O(N) pre-processing:

def cnt(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end

cnt1 = cnt(array1)
#=> {2=>4, 3=>2, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1}
cnt2 = cnt(array2)
#=> {2=>3, 3=>1, 4=>4, 8=>2, 0=>3}

(array1 & array2).flat_map { |n| [n]*[cnt1[n], cnt2[n]].min }
#=> [2,2,2,3,4,8]

Return object after performing intersection of two arrays based on attribute

you can:

1 :

override the eql?(other) method then the array intersection will work

class Link < ApplicationRecord
def eql?(other)
self.class == other.class && self.id == other&.id # classes comparing class is a guard here
end

# you should always update the hash if you are overriding the eql?() https://stackoverflow.com/a/54961965/5872935
def hash
self.id.hash
end
end

2:

use array.select:

array_links.flat_map {|i| selected_links.select {|k|  k.user_id == i.user_id }}

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

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]

Find intersection of arrays of hashes by hash value

simple as that:

user1.connections & user2.connections

if you want only by the id key (other attributes are different)

intersection = user1.connections.map{|oh| oh[:id]} & user2.connections.map{|oh| oh[:id]}
user1.connections.select {|h| intersection.include? h[:id] }

hope it helps!

Get common elements between multiple arrays and common elements between some

Assuming you have an Array of Hashes that looks like this:

a = [
{name: "resource1", lines: ["abc", "def", "ghi"]},
{name: "resource2", lines: ["abc", "jkl", "ghi"]},
{name: "resource3", lines: ["abc", "ghi", "jkl"]}]

Then you could handle the transformation like so:

lines = a.each_with_object({}) do |h,obj| 
h[:lines].each do |line|
(obj[[line]] ||= [])<< h[:name]
end
end.each_with_object({}) do |a, obj|
obj.merge!([a.reverse].to_h) {|_,o,n| o.concat(n)}
end
#=> {["resource1", "resource2", "resource3"]=>["abc", "ghi"], ["resource1"]=>["def"], ["resource2", "resource3"]=>["jkl"]}

First we group all the resources by each line as an Array:

a.each_with_object({}) do |h,obj| 
h[:lines].each do |line|
(obj[[line]] ||= [])<< h[:name]
end
end
#=> {["abc"]=>["resource1", "resource2", "resource3"], ["def"]=>["resource1"], "ghi"=>["resource1", "resource2", "resource3"], ["jkl"]=>["resource2", "resource3"]}

then reverse and merge them as Hashes where duplicate keys result in concatenated Arrays

.each_with_object({}) do |a, obj|
obj.merge!([a.reverse].to_h) {|_,o,n| o.concat(n}
end
#=> {["resource1", "resource2", "resource3"]=>["abc", "ghi"], ["resource1"]=>["def"], ["resource2", "resource3"]=>["jkl"]}


Related Topics



Leave a reply



Submit