Count, size, length…too many choices in Ruby?

2019-03-07 16:31发布

I can't seem to find a definitive answer on this and I want to make sure I understand this to the "n'th level" :-)


    a = { "a" => "Hello", "b" => "World" }
    a.count  # 2
    a.size   # 2
    a.length # 2

    a = [ 10, 20 ]
    a.count  # 2
    a.size   # 2
    a.length # 2

So which to use? If I want to know if a has more than one element then it doesn't seem to matter but I want to make sure I understand the real difference. This applies to arrays too. I get the same results.

Also, I realize that count/size/length have different meanings with ActiveRecord. I'm mostly interested in pure Ruby (1.92) right now but if anyone wants to chime in on the difference AR makes that would be appreciated as well.

Thanks!

6条回答
干净又极端
2楼-- · 2019-03-07 17:09

For arrays and hashes size is an alias for length. They are synonyms and do exactly the same thing.

count is more versatile - it can take an element or predicate and count only those items that match.

> [1,2,3].count{|x| x > 2 }
=> 1

In the case where you don't provide a parameter to count it has basically the same effect as calling length. There can be a performance difference though.

We can see from the source code for Array that they do almost exactly the same thing. Here is the C code for the implementation of array.length:

static VALUE
rb_ary_length(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    return LONG2NUM(len);
}

And here is the relevant part from the implementation of array.count:

static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
    long n = 0;

    if (argc == 0) {
        VALUE *p, *pend;

        if (!rb_block_given_p())
            return LONG2NUM(RARRAY_LEN(ary));

        // etc..
    }
}

The code for array.count does a few extra checks but in the end calls the exact same code: LONG2NUM(RARRAY_LEN(ary)).

Hashes (source code) on the other hand don't seem to implement their own optimized version of count so the implementation from Enumerable (source code) is used, which iterates over all the elements and counts them one-by-one.

In general I'd advise using length (or its alias size) rather than count if you want to know how many elements there are altogether.


Regarding ActiveRecord, on the other hand, there are important differences. check out this post:

查看更多
混吃等死
3楼-- · 2019-03-07 17:15

We have a several ways to find out how many elements in an array like .length, .count and .size. However, It's better to use array.size rather than array.count. Because .size is better in performance.

查看更多
Evening l夕情丶
4楼-- · 2019-03-07 17:17

Adding more to Mark Byers answer. In Ruby the method array.size is an alias to Array#length method. There is no technical difference in using any of these two methods. Possibly you won't see any difference in performance as well. However, the array.count also does the same job but with some extra functionalities Array#count

It can be used to get total no of elements based on some condition. Count can be called in three ways:

Array#count # Returns number of elements in Array

Array#count n # Returns number of elements having value n in Array

Array#count{|i| i.even?} Returns count based on condition invoked on each element array

array = [1,2,3,4,5,6,7,4,3,2,4,5,6,7,1,2,4]

array.size     # => 17
array.length   # => 17
array.count    # => 17

Here all three methods do the same job. However here is where the count gets interesting.

Let us say, I want to find how many array elements does the array contains with value 2

array.count 2    # => 3

The array has a total of three elements with value as 2.

Now, I want to find all the array elements greater than 4

array.count{|i| i > 4}   # =>6

The array has total 6 elements which are > than 4.

I hope it gives some info about count method.

查看更多
smile是对你的礼貌
5楼-- · 2019-03-07 17:19

In most cases (e.g. Array or String) size is an alias for length.

count normally comes from Enumerable and can take an optional predicate block. Thus enumerable.count {cond} is [roughly] (enumerable.select {cond}).length -- it can of course bypass the intermediate structure as it just needs the count of matching predicates.

Note: I am not sure if count forces an evaluation of the enumeration if the block is not specified or if it short-circuits to the length if possible.

Edit (and thanks to Mark's answer!): count without a block (at least for Arrays) does not force an evaluation. I suppose without formal behavior it's "open" for other implementations, if forcing an evaluation without a predicate ever even really makes sense anyway.

查看更多
对你真心纯属浪费
6楼-- · 2019-03-07 17:21

I found a good answare at http://blog.hasmanythrough.com/2008/2/27/count-length-size

In ActiveRecord, there are several ways to find out how many records are in an association, and there are some subtle differences in how they work.

post.comments.count - Determine the number of elements with an SQL COUNT query. You can also specify conditions to count only a subset of the associated elements (e.g. :conditions => {:author_name => "josh"}). If you set up a counter cache on the association, #count will return that cached value instead of executing a new query.

post.comments.length - This always loads the contents of the association into memory, then returns the number of elements loaded. Note that this won't force an update if the association had been previously loaded and then new comments were created through another way (e.g. Comment.create(...) instead of post.comments.create(...)).

post.comments.size - This works as a combination of the two previous options. If the collection has already been loaded, it will return its length just like calling #length. If it hasn't been loaded yet, it's like calling #count.

Also I have a personal experience:

<%= h(params.size.to_s) %> # works_like_that !
<%= h(params.count.to_s) %> # does_not_work_like_that !
查看更多
劳资没心,怎么记你
7楼-- · 2019-03-07 17:25

There is a crucial difference for applications which make use of database connections.

When you are using many ORMs (ActiveRecord, DataMapper, etc.) the general understanding is that .size will generate a query that requests all of the items from the database ('select * from mytable') and then give you the number of items resulting, whereas .count will generate a single query ('select count(*) from mytable') which is considerably faster.

Because these ORMs are so prevalent I following the principle of least astonishment. In general if I have something in memory already, then I use .size, and if my code will generate a request to a database (or external service via an API) I use .count.

查看更多
登录 后发表回答