Skip to content

Instantly share code, notes, and snippets.

@bkeepers

bkeepers/gist:6002211

Last active Dec 19, 2015
Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@carlosantoniodasilva carlosantoniodasilva commented Jul 15, 2013

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

This comment has been minimized.

Copy link

@nakajima nakajima commented Jul 15, 2013

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

This comment has been minimized.

Copy link

@mmozuras mmozuras commented Jul 15, 2013

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

This comment has been minimized.

Copy link

@rwjblue 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

This comment has been minimized.

Copy link

@teleological teleological commented Jul 15, 2013

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

This comment has been minimized.

Copy link

@dchelimsky dchelimsky commented Jul 15, 2013

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

This comment has been minimized.

Copy link

@ungarst 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