Кирчо обнови решението на 27.10.2012 16:00 (преди около 12 години)
+class Song
+ attr_reader :name, :artist, :album
+
+ def initialize(name, artist, album)
+ @name, @artist, @album = name, artist, album
+ end
+
+ def match_criterias?(criterias_list)
+ criterias_list.any? do |criterias|
+ conjunction_true? criterias
+ end
+ end
+
+ private
+ def conjunction_true?(list)
+ list.all? { |criteria| match_single_criteria? criteria }
+ end
+
+ def match_single_criteria?(criteria)
+ if criteria.attribute.include? "_not"
+ !(match_single_criteria? !criteria)
+ else
+ send "#{criteria.attribute}_matches?", criteria.value
+ end
+ end
+
+ def name_matches?(name)
+ @name == name
+ end
+
+ def artist_matches?(artist)
+ @artist == artist
+ end
+
+ def album_matches?(album)
+ @album == album
+ end
+end
+
+class CriteriaList
+ attr_reader :list
+
+ def initialize(list)
+ @list = list
+ end
+
+ def |(other)
+ CriteriaList.new(@list | other.list)
+ end
+
+ def &(other)
+ arr, limit = [], @list.size * other.list.size - 1
+ 0.upto(limit) do |i|
+ arr << (@list[i % @list.size] | other.list[(limit - i) % other.list.size])
+ end
+ CriteriaList.new(arr)
+ end
+
+ def !
+ list = @list.map { |criterias| negate_criterias criterias }
+ negated_list = list.map { |current| CriteriaList.new([current]) }
+ negated_list.inject(&:&)
+ end
+
+ private
+ def negate_criterias(criterias)
+ criterias.map { |criteria| !criteria }
+ end
+end
+
+class Criteria
+ attr_reader :attribute, :value
+
+ def self.name(name)
+ CriteriaList.new [[Criteria.new("name", name)]]
+ end
+
+ def self.artist(artist)
+ CriteriaList.new [[Criteria.new("artist", artist)]]
+ end
+
+ def self.album(album)
+ CriteriaList.new [[Criteria.new("album", album)]]
+ end
+
+ def initialize(attribute, value)
+ @attribute = attribute
+ @value = value
+ end
+
+ def !
+ if @attribute.include? "_not"
+ Criteria.new(@attribute.gsub("_not", ""), @value)
+ else
+ Criteria.new(@attribute + "_not", @value)
+ end
+ end
+end
+
+class Collection
+ include Enumerable
+ attr_reader :songs
+
+ def self.parse(text)
+ lines = text.split("\n")
+ songs = lines.each_slice(4).map do |song|
+ Song.new(song[0], song[1], song[2])
+ end
+ Collection.new(songs)
+ end
+
+ def initialize(songs)
+ @songs = songs
+ end
+
+ def names
+ @songs.map { |song| song.name }.uniq
+ end
+
+ def artists
+ @songs.map { |song| song.artist }.uniq
+ end
+
+ def albums
+ @songs.map { |song| song.album }.uniq
+ end
+
+ def filter(criteria)
+ filtered = @songs.select { |song| song.match_criterias? criteria.list }
+ Collection.new(filtered)
+ end
+
+ def adjoin(collection)
+ Collection.new(@songs | collection.songs)
+ end
+
+ def each
+ 0.upto(@songs.length-1) { |current| yield @songs[current] }
+ end
+end