11

I defined a method that takes an Array (of Strings), like

def list(projects)
  puts projects.join(', ')
end

list(['a', 'b'])

However, as a short-hand for calling it with an Array that only consists of a single String element, I'd like the same function to also accept a single plain String like

  list('a')

What would be the Ruby way to handle this inside the method?

4
  • 3
    Array(projects).join(', ') Commented Jul 29, 2015 at 15:13
  • 1
    Make that comment into an answer, it's the right way. Commented Jul 29, 2015 at 15:34
  • Seriously, @LeeJarvis, post that as an answer so we can upvote it. Commented Jan 11, 2017 at 23:05
  • @DavidMoles: 18 months later, I think it's safe to say Lee won't mind if someone else takes his comment and posts it as an answer. Frankly, it's not against community standards to use someone else's comment as the basis for an answer, even if the comment is only minutes old. A decent answer takes more effort to write up than an off-hand comment, and often the commenter has no intention of investing that effort. More power to the person who runs with the idea and provides something useful. Commented Jan 13, 2017 at 20:36

5 Answers 5

10

Why not something like this:

def list(*projects)
  projects.join(', ')
end

Then you can call it with as many arguments as you please

list('a')
#=> "a"
list('a','b')
#=> "a, b"
arr = %w(a b c d e f g)
list(*arr)
#=> "a, b, c, d, e, f, g"
list(arr,'h','i')
#=> "a, b, c, d, e, f, g, h, i"

The splat (*) will automatically convert all arguments into an Array, this will allow you to pass an Array and/or a String without issue. It will work fine with other objects too

list(1,2,'three',arr,{"test" => "hash"}) 
#=> "1, 2, three, a, b, c, d, e, f, g, {\"test\"=>\"hash\"}"

Thank you @Stefan and @WandMaker for pointing out Array#join can handle nested Arrays

Sign up to request clarification or add additional context in comments.

7 Comments

@SergioTulentsev I know i just wanted to show all the ways it can be used without any concern for errors. Plus originally I didn't have the flatten in there and then I figured since the OP asked to pass a true array this would be the easiest way to handle this.
Why is flatten needed? When I tried projects.join(', ') it seems to give desired result
@WandMaker without flatten, list(['a', 'b']) would result in a nested array. Removing flatten works in this case because join can handle nested arrays.
_this will allow you to pass an Array and/or a String without issue_—I don't think this is strictly true. It works because join handles nested arrays, but consider something like def count_args(*p); p.size; endcount_args(*arr) will return 7, but count_args(arr) will return 1. The caller still needs to know to splat the array.
@engineersmnky I'm sorry you're taking it personally. If you run across one of my answers while searching for information and find it steers you in the wrong direction, by all means comment and downvote.
|
4

You can check the type of the input, for example:

>> a = ['foo', 'bar']
=> ['foo', 'bar']
>> a.is_a? Array
=> true

you can also check string with is_a?

You would end up with something link:

def list(projects)
    if projects.is_a? Array
        puts projects.join(', ')
    else
        puts projects
    end
end

list('a') # a
list(['a', 'b']) # a, b

You have many ways of doing this in Ruby, respond_to? and kind_of? also work

Comments

3

One way would be you could type-check the input:

def list(projects)
  if projects.is_a?(String)
    projects = [projects]
  end
  puts projects.join(', ')
end

Or you could use Array() to auto-convert a string into an array, or leave an existing array alone:

def list(projects)
  Array(projects).join(', ')
end

Comments

2
def list(projects)
  puts Array(projects).join(', ')
end

list ['a', 'b']
a, b
list 'a'
a

One could alternatively write

puts [*projects].join(', ')

A caveat: don't use this.

I posted this solution only to show two ways it could be done. @sawa, in his now-deleted answer, is right: users of the method should not be permitted to pass an array as an argument. Instead, they should be restricted to passing individual elements. If:

arr = [1,2,3]

the method would be called by one of the following:

list 1
list 1,2
list 1,2,3
list *arr

but not:

list arr

This simplifies the method and makes its application more consistent. Ask yourself this: why permit arr in lieu of *arr? To save one character?

The method that does this is @engineersmnky's answer with .flatten removed.

17 Comments

I've voted to undelete sawa's answer, accepting either an array or a list of objects is the way to go. Trying to handle both is cumbersome.
@Stefan, I didn't know one can vote to undelete an answer. That's weird, but there are now two votes. Perhaps sawa will yield to our wishes and voluntarily undelete it.
For the record, I didn't suggest removing flatten :)
@Sergio, I don't know how I totally misread your comment, but since a string doesn't respond to flatten, I don't understand what your meant.
This is the longest conversation about any one of my answers and it's not even on my answer. :)
|
1

I would ask the provided value if it responds to join:

def list(projects)
  projects.respond_to?(:join) ? projects.join(', ') : projects 
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.