Ruby is the fastest way to select elements belonging to two arrays with a nested attribute

My Ruby request is very slow. I am looking for a faster way to select elements belonging to two arrays

listA is a list of objects A with attribute name

listB is a list of objects B with attribute name

There listAmay be several elements in which all are the same name, but nothing is listBthe same name. There are also elements in listAwhose are namenot in listB. I want the function (loop) to return all the elements in listAwhere they nameare in listB.

This is my current solution, but it is too slow. How can I do it faster?

z = 0
new = []
while z < listB.length 
    hold = listA.select{|obj| obj.name == listB[z].name}
    new += hold
    z += 1
end
+4
source share
3 answers

. , , - O (1) . , , 1000 , , 350 , include? n=10000

2: , .

names = {}
listB.each{|item| names[item.name] = true}
result = listA.select{|item| names[item.name]}

. .

  • 03/08/2015 - 12341234

class MyObject
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

n=10000
k=n/10

listA = (1..n).map{ MyObject.new(('a'..'z').to_a.shuffle[0,8].join)}
listB = listA.sample(k)
listB += (1..(n-k)).map{ MyObject.new(('a'..'z').to_a.shuffle[0,8].join)}

require 'benchmark'
require 'set'

Benchmark.bm do |x|
    x.report("Hash") do
        names = {}
        listB.each{|item| names[item.name] = true}
        result = listA.select{|item| names[item.name]}
    end

    x.report("OP code") do
        z = 0
        new = []
        while z < listB.length 
            hold = listA.select{|obj| obj.name == listB[z].name}
            new += hold
            z += 1
        end
    end

    x.report("Include") do
      names = listB.map(&:name)
      listA.select{|obj| names.include?(obj.name) }
    end

    x.report("Set") do 
        names = listB.map(&:name).to_set
        listA.select{|item| names.include?(item.name)}
    end
end

Benchmark

:

  • Intel Core i7 920 @2,67
  • 13
  • Windows 8 x64
  • Ruby 21 x64

:

Algorithm       user     system      total        real
Hash         0.015000   0.000000   0.015000 (  0.013002)
OP code   26.053000   0.016000  26.069000 ( 26.161283)
Include      9.219000   0.000000   9.219000 (  9.244161)
Set          0.016000   0.000000   0.016000 (  0.013001)
+5

- :

names = listB.map(&:name)
listA.select {|obj| names.inlude? obj.name }
+2

, . :

require 'set'
names = listB.map(&:name).to_set
listA.select{|item| names.include?(item.name)}

@Cyrill_DD, :

       user     system      total        real
Set   0.010000   0.000000   0.010000 (  0.012556)
Hash  0.010000   0.000000   0.010000 (  0.011330)
+1

Source: https://habr.com/ru/post/1598450/


All Articles