DEV Community

loading...

Ruby: Getting Collect, Map, and Select Set Straight

morinoko profile image Felice Forby ・2 min read

In one of my Flatiron coding challenges, I had to make an anagram tester that would test which words in an array were anagrams of the target word. Possible anagrams were passed to a #match method through an array. The #match method would then be called on an Anagram object and “collect” all the words that matched as an anagram. For example:

listen = Anagram.new("listen")
listen.match(%w(enlists google inlets banana))

# => ["inlets"]
Enter fullscreen mode Exit fullscreen mode

As I wanted to “collect” all the matches, at first I attempted to use #collect on the array.

class Anagram
  attr_accessor :word

  def initialize(word)
    @word = word
    @letters = @word.split("").sort
  end

  # find anagram matches given an array of words
  def match(possible_anagrams)
    possible_anagrams.collect do |possible_anagram|
      @letters == possible_anagram.split("").sort
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

The problem with #collect, however, is that it collects all of the return values produced by the code in the block, so I was getting an array full of the values that were returned by the block's expression.

With #collect, the listen.match(%w(enlists google inlets banana)) was returning => [false, false, true, false]. What I really needed was to get just the items in the array that made the code block return true.

Guess who can do that? You guessed it: #select! Switching out #collect for #select solved the problem:

class Anagram
  attr_accessor :word

  def initialize(word)
    @word = word
    @letters = @word.split("").sort
  end

  # find anagram matches given an array of words
  def match(possible_anagrams)
    possible_anagrams.select do |possible_anagram|
      @letters == possible_anagram.split("").sort
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Now only the items that made the block true would be included in the returned array: => ["inlets"].

The difference is super clear if you check the Ruby documentation:

  • #collect: “Creates a new array containing the values returned by the block.”
  • #select: “Returns a new array containing all elements of the array for which the given block returns a true value.”

Okay, so what about #map? It turns out #map is the exact same thing as #collect. A lot of times you’ll hear that certain functions do the same thing, but in truth there are actually slight differences. In the case of #map and #collect, though, they are identical down to the source code. Check for yourself by clicking the “click to toggle source” under the #collect and #map documentation!

Edit: Initial code refactored based on discussion comments below (thank you!)

Discussion (2)

pic
Editor guide
Collapse
morinoko profile image
Felice Forby Author

Ah, good catch on splitting the word too many times. It would definitely be better to split the word in the initialize method so it's only done once. Also, yes, the block does not need to return "real" values - your suggestion would simplify the code. I'll fix this up based on your feedback. Thank you!