Skip to content

Instantly share code, notes, and snippets.

@bhb
Last active December 24, 2015 13:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bhb/6802338 to your computer and use it in GitHub Desktop.
Save bhb/6802338 to your computer and use it in GitHub Desktop.
A toy example of defining filter and sorting operations in one place (on a class) while still keeping collections generic.
# An idea on how to sort and filter Ruby collections in a functional
# style
# Just using Struct for convenience, this could be a normal class.
User = Struct.new(:first_name, :last_name, :admin) do
def self.sort_by_name
# Define a new sort
lambda do |x,y|
# last name, first name DESC
(y.last_name + " " + y.first_name) <=> (x.last_name + " " + x.first_name)
end
end
def self.default_sort
sort_by_name
end
def self.not_admin
lambda { |x| !x.admin }
end
end
joe = User.new("Joe", "Smith", false)
bob = User.new("Bob", "Jones", true)
alice = User.new("Alice", "Fields", false)
arr = [joe, bob, alice]
# The logic for selection is within class
puts arr.select(&User.not_admin).inspect
# The logic for sorting is also in one place in the class
puts arr.sort(&User.sort_by_name).inspect
puts arr.sort(&User.default_sort).inspect
@danmayer
Copy link

danmayer commented Oct 3, 2013

interesting so basically in some controller you would have your collection and still call .sort, but you could pass in some shared module with predefined sorts on it. Default sort could even inspect arr[0] and select an appropriate default sort or something

@bhb
Copy link
Author

bhb commented Oct 4, 2013

Yeah, my initial thought was that you'd have to pass in the specific sort in the controller every time (but the definition of how to sort would be kept on the class so it's defined once), but certainly you could even have the collection defer to the first element.

# haven't actually run this ...
class AwesomeArray < Array

   alias_method :old_sort, :sort
   def sort(&block)
      if block
        old_sort(&block)
      else
        sort(&first.class.default_sort)
      end
   end

end

AwesomeArray.new([joe, bob, alice]).sort

or pass in a set of defaults

# haven't actually run this ...
class AwesomeArray < Array

   def with_defaults=(d)
     @defaults = d
     self
   end

   alias_method :old_sort, :sort
   def sort(&block)
      if block
        old_sort(&block)
      elsif @defaults
        old_sort(&@defaults.default_sort)
      end
   end
end

AwesomeArray.new([joe, bob, alice]).with_defaults(User).sort

Yes, maybe you need to make a new collection type that supports this, but the point is that you can use the same collection type for Books or Users or Cars or anything else.

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