25

Suppose you have an array of objects in Rails @objects

If I want to display the first 5 objects, what is the difference between using:

  1. @objects.limit(5)
  2. @objects.take(5)
  3. @objects.first(5)

I am talking about the front end (Ruby), NOT SQL. The reason why the objects are not limited in SQL is because the same array may be used elsewhere without applying a limit to it.

Does it have anything to do with object instantiation?

Prem
  • 3,373
  • 7
  • 29
  • 43
  • 1
    Are you sure @objects is an Array? I've never heard of a #limit method. #first however is a standard method to return the first element. #take is also a method. – seand May 15 '13 at 01:35
  • Yes `@objects` is an array. I applied the `#limit` method and it seemed to have performed the same task as `#take` and `#first`. (In the view not the controller). – Prem May 15 '13 at 01:40
  • 2
    @objects is probably not an array but rather an ActiveRecord relation. That is why you use of limit(5) is working. – Larry McKenzie May 15 '13 at 01:51

2 Answers2

30
  1. limit is not an array method
  2. take requires an argument; it returns an empty array if the array is empty.
  3. first can be called without an argument; it returns nil if the array is empty and the argument is absent.

Source for 2.0 take

              static VALUE
rb_ary_take(VALUE obj, VALUE n)
{
    long len = NUM2LONG(n);
    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to take negative size");
    }
    return rb_ary_subseq(obj, 0, len);
}

Source for 2.0 first:

              static VALUE
rb_ary_first(int argc, VALUE *argv, VALUE ary)
{
    if (argc == 0) {
        if (RARRAY_LEN(ary) == 0) return Qnil;
        return RARRAY_PTR(ary)[0];
    }
    else {
        return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
    }
}

In terms of Rails:

  1. limit(5) will add the scope of limit(5) to an ActiveRecord::Relation. It can not be called on an array, so limit(5).limit(4) will fail.

  2. first(5) will add the scope of limit(5) to an ActiveRecord::Relation. It can also be called on an array so .first(4).first(3) will be the same as .limit(4).first(3).

  3. take(5) will run the query in the current scope, build all the objects and return the first 5. It only works on arrays, so Model.take(5) will not work, though the other two will work.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
Shawn Balestracci
  • 7,380
  • 1
  • 34
  • 52
  • Are you sure `#limit` is not an array method? I applied it to a view on an object array `@objects` and it performed the same task as `#take` and `#first`. `#first` can take an argument (optional). Try it yourself – Prem May 15 '13 at 01:46
  • `take()` can be called without an argument. – kingsfoil Oct 01 '14 at 19:42
  • @alex0112 Not in ruby 1.8.7, 1.9.3, 2.0.0 or 2.1.0 – Shawn Balestracci Oct 02 '14 at 03:11
  • According to [guides](http://guides.rubyonrails.org/active_record_querying.html#retrieving-a-single-object) it can be called without an option. According to [rubydoc](http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-take) however, it does take an arg. I stand corrected. – kingsfoil Oct 02 '14 at 15:05
  • calling `#take` on a relation with also add a `limit` to the query – user1063998 May 16 '17 at 17:47
6

The answer chosen answer seems to be outdated (in terms of Rails) so I would like to update some information.

  1. limit on an ActiveRecord::Relation will still be a Relation. So if you call:

    Model.limit(5).limit(4)
    

    will be same as:

    Model.limit(4)
    
  2. first on an ActiveRecord::Relation will make the result an Array. So you cannot call any scope after first like:

    Model.first(5).where(id: 1)
    

    But you can do this:

    Model.limit(5).where(id: 1)
    
  3. take

    Model.take(5)
    

    works now. It will return an array, so you cannot call any scope either.


On an ActiveRecord::Relation object, if you call first it will include ORDER BY 'tables'.id. Whereas with limit and take there is no ORDER BY included but the sorting is depended by the database sorting implementation.

Corubba
  • 2,229
  • 24
  • 30
Dang Duy
  • 61
  • 1
  • 1
  • In calling `limit` twice, you aren't chaining relations, you are overwriting the previous value. `Model.limit(4).limit(5)` returns 5 elements. – Ray Baxter Aug 06 '22 at 22:39