Никола обнови решението на 26.10.2012 11:52 (преди над 12 години)
+class Criteria
+ attr_reader :match
+
+ def initialize match
+ @match = match
+ end
+
+ def self.name name
+ Criteria.new lambda { |song| song.name == name }
+ end
+
+ def self.artist artist
+ Criteria.new lambda { |song| song.artist == artist }
+ end
+
+ def self.album album
+ Criteria.new lambda { |song| song.album == album }
+ end
+
+ def | criteria
+ Criteria.new lambda { |song| match.(song) | criteria.match.(song) }
+ end
+
+ def & criteria
+ Criteria.new lambda { |song| @match.(song) & criteria.match.(song) }
+ end
+
+ def !
+ Criteria.new lambda { |song| !@match.(song) }
+ end
+end
+
+class Song
+ attr_reader :name, :artist, :album
+
+ def initialize name, artist, album
+ @name, @artist, @album = name, artist, album
+ end
+end
+
+class Collection
+ include Enumerable
+ attr_reader :songs
+
+ def initialize
+ @songs = []
+ end
+ private :initialize
+
+ def names
+ map(&:name).uniq
+ end
+
+ def artists
+ map(&:artist).uniq
+ end
+
+ def albums
+ map(&:album).uniq
+ end
+
+ def each
+ @songs.each do |song|
+ yield song
+ end
+ end
+
+ def filter criteria
+ Collection.from_a select(&criteria.match)
+ end
+
+ def self.parse text
+ result = Collection.new
+
+ text.split("\n").each_slice 4 do |name, artist, album|
+ result.songs << Song.new(name, artist, album)
+ end
+
+ result
+ end
+
+ def adjoin collection
+ collection = collection.reject { |x| member? x }
+ Collection.from_a to_a.concat collection
+ end
+
+ def self.from_a array
+ array.each_with_object(Collection.new) do |song, result|
+ result.songs << song
+ end
+ end
+end