303

What is the right way to:

is_array("something") # => false         (or 1)

is_array(["something", "else"]) # => true  (or > 1)

or to get the count of items in it?

BuddyJoe
  • 69,735
  • 114
  • 291
  • 466

7 Answers7

578

You probably want to use kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true
siery
  • 467
  • 3
  • 20
ry.
  • 7,885
  • 1
  • 20
  • 10
  • 46
    There's also `is_a?` and `instance_of?`. See http://stackoverflow.com/questions/3893278/ruby-kind-of-vs-instance-of-vs-is-a – Nathan Long Mar 21 '11 at 15:57
  • 2
    Type checking is for Java. Go ahead and just call count on the variable. Write unit tests to make sure the method works as expected. – user132447 Mar 21 '12 at 14:55
  • 17
    @user132447 actually java is type safe so you don't need to worry about checking any types – grinch Aug 15 '12 at 23:46
  • 8
    I downvoted this now since I don't think this is a good practice in a language like Ruby. The answer by @zgchurch is clearly a much more idiomatic approach to the question. In cases like this, I think it makes much more sense to try and figure out what the OP means, rather than blindly giving him a shotgun... – Per Lundberg Jul 01 '14 at 10:39
  • Just for the record, if you're checking for a string, it's `s.kind_of?(String)` – FloatingRock Sep 02 '14 at 18:38
  • This feels more like Java. The Ruby way is to use [duck typing](http://c2.com/cgi/wiki?DuckTyping). So better use `respond_to?` to check your assumptions. – geekQ Dec 18 '14 at 09:31
  • 1
    Why would you want to use `kind_of?()` over other solutions? Some explanation on the benefits of your answer over others would be helpful for future readers. – AlbertEngelB Sep 09 '15 at 19:11
  • 1
    this answer has the added bonus of working in old versions. the answer below is invalid prior to version 1.9. – omikes Feb 02 '18 at 00:53
159

Are you sure it needs to be an array? You may be able to use respond_to?(method) so your code would work for similar things that aren't necessarily arrays (maybe some other enumberable thing). If you do actually need an array, then the post describing the Array#kind\_of? method is best.

['hello'].respond_to?('each')
potashin
  • 44,205
  • 11
  • 83
  • 107
zgchurch
  • 2,302
  • 1
  • 14
  • 7
  • 1
    In this case I am sure it will be an Array. But nice to know this method too. +1 – BuddyJoe Oct 07 '09 at 15:58
  • Interesting idea, I'm using push/pop on a data structure. Would anything besides arrays respond to those methods? – Drew Sep 29 '10 at 11:59
  • 3
    If you want something more array-like, you may want `respond_to?(:to_ary)`. – Andrew Grimm Jan 19 '11 at 10:47
  • 26
    In general, this is a good practice for OO development. I read where somebody said basically: don't imagine that you're calling methods on your objects. You're sending them messages. If an object knows how to respond to your message, you don't care what class it is, or whether it has a method named that, or whether it is dynamically creating a response via method_missing. The important thing is, can it respond to your message? This allow better abstraction of function and implementation. You can change what object you use later, as long as it still respond correctly. – Nathan Long Mar 21 '11 at 14:25
  • 2
    The only issue with this is say I want to check if something is an indexed iterable, so arrays, linked lists, etc. would be cool, but I don't want key value stores like hashes? – Colton Voege Jan 08 '17 at 03:52
63

Instead of testing for an Array, just convert whatever you get into a one-level Array, so your code only needs to handle the one case.

t = [*something]     # or...
t = Array(something) # or...
def f *x
    ...
end

Ruby has various ways to harmonize an API which can take an object or an Array of objects, so, taking a guess at why you want to know if something is an Array, I have a suggestion.

The splat operator contains lots of magic you can look up, or you can just call Array(something) which will add an Array wrapper if needed. It's similar to [*something] in this one case.

def f x
  p Array(x).inspect
  p [*x].inspect
end
f 1         # => "[1]"
f [1]       # => "[1]"
f [1,2]     # => "[1, 2]"

Or, you could use the splat in the parameter declaration and then .flatten, giving you a different sort of collector. (For that matter, you could call .flatten above, too.)

def f *x
  p x.flatten.inspect
end         # => nil
f 1         # => "[1]"
f 1,2       # => "[1, 2]"
f [1]       # => "[1]"
f [1,2]     # => "[1, 2]"
f [1,2],3,4 # => "[1, 2, 3, 4]"

And, thanks gregschlom, it's sometimes faster to just use Array(x) because when it's already an Array it doesn't need to create a new object.

Community
  • 1
  • 1
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
39

[1,2,3].is_a? Array evaluates to true.

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
dipole_moment
  • 5,266
  • 4
  • 39
  • 55
  • 1
    What does this add to the answers that have been on the site for almost seven years..? – Martin Tournoij Mar 16 '16 at 01:04
  • 11
    @Carpetsmoker there isn't a concise answer that references ``is_a?`` in this whole thread. The closest is a ``[1,2,3].is_a? Enumerable``. I still think it's worth while to have this answer. – dipole_moment Mar 16 '16 at 15:04
  • 5
    You know .. you're actually right ... I could've sworn I saw that up there earlier :-/ Have an upvote! – Martin Tournoij Mar 16 '16 at 15:05
19

It sounds like you're after something that has some concept of items. I'd thus recommend seeing if it is Enumerable. That also guarantees the existence of #count.

For example,

[1,2,3].is_a? Enumerable
[1,2,3].count

note that, while size, length and count all work for arrays, count is the right meaning here - (for example, 'abc'.length and 'abc'.size both work, but 'abc'.count doesn't work like that).

Caution: a string is_a? Enumerable, so perhaps this isn't what you want... depends on your concept of an array like object.

Peter
  • 127,331
  • 53
  • 180
  • 211
11

Try:

def is_array(a)
    a.class == Array
end

EDIT: The other answer is much better than mine.

Lucas Jones
  • 19,767
  • 8
  • 75
  • 88
6

Also consider using Array(). From the Ruby Community Style Guide:

Use Array() instead of explicit Array check or [*var], when dealing with a variable you want to treat as an Array, but you're not certain it's an array.

# bad
paths = [paths] unless paths.is_a? Array
paths.each { |path| do_something(path) }

# bad (always creates a new Array instance)
[*paths].each { |path| do_something(path) }

# good (and a bit more readable)
Array(paths).each { |path| do_something(path) }
GuyPaddock
  • 2,233
  • 2
  • 23
  • 27
  • This will produce unexpected results when passing a Hash because `to_a` is called on each argument added to the new array, so `Array({id: 100})` returns `[[:id, 100]]` – Brent Jun 05 '19 at 00:49