Skip navigation

ruby ugliness

Wouldn’t it be nice to define a Hash for which each heretofore undefined element is a Set? Probably should be something like Hash.new(Set.new), right? Then we could write this function:

  def group_by_basename(paths, result = Hash.new(Set.new))
    first, *rest = paths
    result[File.basename(first)] << first
    rest.empty? ? result : group_by_basename(rest, result)
  end

And test it with a bunch of paths:

  group_by_basename(%w| a/a.txt a/b.txt a/b/b.txt |)
   # => {}

Doesn’t work as we expected. Why not? Well, it all has to do with our expectations about Set.new. It yields a value which is simply a new Set, as opposed to yielding a value that yields a new Set every time it is accessed. So actually, to get what we want from Ruby we have to use the more verbose Hash.new{|h, k| h[k] = Set.new }. Our example, again:

  def group_by_basename(paths, result = Hash.new{|h, k| h[k] = Set.new })
    first, *rest = paths
    result[File.basename(first)] << first
    rest.empty? ? result : group_by_basename(rest, result)
  end
  group_by_basename(%w| a/a.txt a/b.txt a/b/b.txt |)
   # => {"a.txt"=>#<Set: {"a/a.txt"}>, "b.txt"=>#<Set: {"a/b/b.txt", "a/b.txt"}>}
Advertisements

Post a Comment

You must be logged in to post a comment.
%d bloggers like this: