Skip to content

Instantly share code, notes, and snippets.

@bkeepers
Last active December 19, 2015 18:58
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save bkeepers/6002211 to your computer and use it in GitHub Desktop.
I'm looking for a better pattern for defining a method that takes a single object or an array of objects as an argument, does something with them, and then returns either a single object or an Array depending on what was passed to it.
def dress(dog_or_dogs)
dressed_dogs = Array(dog_or_dogs).map {|dog| DogSweater.new(dog) }
dog_or_dogs.respond_to?(:each) ? dressed_dogs : dressed_dogs.first
end
one_dog = dress(Dog.new)
all_my_dogs = dress([Dog.new, Dog.new, Dog.new])
@carlosantoniodasilva
Copy link

How about:

def dress(dog_or_dogs)
  if dog_or_dogs.respond_to? :each
    dog_or_dogs.map { |dog| DogSweater.new(dog) }
  else
    DogSweater.new(dog_or_dogs)
  end
end

Or you could change the map block call dress(dog) again if finding that's duplication.

@nakajima
Copy link

Isn't this a little clearer?

def dress(dog)
  DogSweater.new(dog)
end

def dress_all(dogs)
  dogs.map { |dog| dress(dog) }
end

one_dog = dress(Dog.new)
all_my_dogs = dress_all([Dog.new, Dog.new, Dog.new])

@mmozuras
Copy link

class Dog
  def dress
    DogSweater.new(self)
    self
  end
end

class Dogs < Struct.new(:dogs)
  def dress
    @dogs.map { |dog| dog.dress }
  end
end

dog = Dog.new
dogs = Dogs.new([Dog.new, Dog.new, Dog.new])

one_dog = dog.dress
all_my_dogs = dogs.dress

Or if you're feeling slightly risque:

class Dog
  include Enumerable

  def dress
    DogSweater.new(self)
    self
  end

  def each(&block)
    block.call(self)
  end
end

dog = Dog.new
dogs = Dogs.new([Dog.new, Dog.new, Dog.new])

one_dog = dog.map { |dog| dog.dress }
all_my_dogs = dogs.map { |dog| dog.dress }

Since the example is contrived, not sure how far to go with this 😏 With these implementations, I'm making an assumption, there's more to it than just a simple Array(...).map could solve.

@rwjblue
Copy link

rwjblue commented Jul 15, 2013

A small variation of @nakajima's example is what I have used in the past for this sort of thing:

def dress(input)
  input.respond_to?(:each) ? dress_all(input) : dress_one(input)
end

def dress_one(dog)
  DogSweater.new(dog)
end

def dress_all(dogs)
  dogs.map { |dog| dress(dog) }
end

@teleological
Copy link

def dress(*dogs)
  dogs.size == 1 ? dress_dog(dogs[0]) : dogs.map { |dog| dress_dog(dog) }
end

def dress_dog(dog)
  DogSweater.new(dog)
end

dressed_dog = dress(Dog.new)
dressed_dogs = dress(Dog.new, Dog.new, Dog.new)
more_dressed_dogs = dress(*other_dogs)

@dchelimsky
Copy link

I'm looking for a better pattern for defining a method that takes a single object or an array of objects as an argument

Don't? Why not just UTFL™?

all_my_dogs = [Dog.new, Dog.new, Dog.new].map(&:dress)

This uses core functionality of the language to handle a core function (do something to every element in this collection and return the collection) and doesn't require that I understand the meaning of different inputs to dress. All for the modest cost of 6 additional characters.

@ungarst
Copy link

ungarst commented Jul 15, 2013

@rjackson why call dress from within dress_all rather than dress_one. Calling dress one seems simpler to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment