schnika
schnika

Reputation: 79

Is there a more performant way of creating this array?

for one of my views I want to include a search field with jqueries typeahead function.

The array should contain all the attribute values of a client.

The array for the query is generated the following way:

@clients = []
Client.each do |client|
  @clients << client.attributes.values.join(' ')
end

Is this approach performant enough for a dataset of about 3000 entries? Or is there a better and faster solution?

Thanks in advance. Cheers, Patrick

Update

One user mentioned to implement it like this:

@clients = Client.map do |client|
  client.attributes.values.join(' ')
end

This is another way to do it. But a benchmark reveals that this is no improvement in performance.

This leaves me with the question: Maybe there is a more performant way, but speaking about a maxium of 3000 records - does it really matter?

Upvotes: 1

Views: 102

Answers (3)

mind.blank
mind.blank

Reputation: 4880

You probably don't need to retrieve all the attributes (for example 'updated_at'), so the following may be faster:

@clients = Client.select([:name, :email, :id]).map do |client|
  client.attributes.values.join(' ')
end

Added the id in case you need to link to the client.

Upvotes: 1

Patrick Oscity
Patrick Oscity

Reputation: 54684

Even if ActiveRecord models would implement the map method (which they don't i believe), the two solutions suggested by the OP and @xdazz are time- and memory-complexity-wise equivalent. This can be observed with this simple benchmark:

require 'fruity'

# Dummy client class
class Client < Struct.new(:first_name, :last_name, :position, :company)
  class << self
    include Enumerable

    def each(&block)
      5000.times do
        yield Client.new('Firstname', 'Lastname', 'CEO', 'Company Inc.')
      end
    end
  end

  alias_method :attributes, :to_h
end


compare do
  schnika do
    clients = []
    Client.each do |client|
      clients << client.attributes.values.join(' ')
    end
    nil
  end

  xdazz do
    clients = Client.map do |client|
      client.attributes.values.join(' ')
    end
    nil
  end
end

Which will output

schnika is similar to xdazz

Also, when you look at the implementation of map (synonymous to collect), it becomes clear that really nothing else happens than in the OP's method:

static VALUE
rb_ary_collect(VALUE ary)
{
    long i;
    VALUE collect;

    RETURN_ENUMERATOR(ary, 0, 0);
    collect = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
    rb_ary_push(collect, rb_yield(RARRAY_PTR(ary)[i]));
    }
    return collect;
}

This translates to:

class Array
  def collect
    collect = []
    self.each do |el|
      collect << yield(el)
    end
    collect
  end
end

Upvotes: 4

xdazz
xdazz

Reputation: 160843

You could use .map:

@clients = Client.map do |client|
  client.attributes.values.join(' ')
end

Upvotes: 5

Related Questions