Решение на Втора задача от Генади Самоковаров

Обратно към всички решения

Към профила на Генади Самоковаров

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 12 успешни тест(а)
  • 0 неуспешни тест(а)

Код

class Song
attr_accessor :name, :artist, :genre, :subgenre, :tags
def initialize(format = nil)
return if format.nil?
@name, @artist, @genre, @subgenre, @tags = parse format
end
def [](key)
send(key) rescue nil
end
private
def parse(format)
[].tap do |result|
name, artist, genre, tags = format.split('.').map(&:strip)
result << name << artist
genre.split(',').map(&:strip).tap do |parsed_genre|
genre, subgenre = parsed_genre
result << genre << subgenre
tags = tags.to_s if tags.nil?
tags.split(',').map(&:strip).tap do |tags|
tags.concat parsed_genre
result << tags.map(&:downcase)
end
end
end
end
end
class Collection
attr_reader :songs
def initialize(catalog, additional_tags = {})
@songs = catalog.lines.map { |format| Song.new format }
additional_tags.each do |artist, tags|
find_by_artist(artist).map { |song| song.tags.concat tags }
end
end
def find(criteria)
return songs if criteria.empty?
result = []
criteria.each do |field, query|
if result.empty?
result.concat send("find_by_#{field}", query)
else
result &= send("find_by_#{field}", query)
end
end
result
end
%w{name artist genre subgenre}.each do |field|
define_method("find_by_#{field}") do |query|
songs.select { |song| song[field] == query }
end
end
def find_by_tags(query)
selected, rejected = [].tap do |me|
Array(query).tap do |it|
me << it.reject { |tag| tag.end_with? '!' }
me << it.map { |tag| tag.chomp '!' } - me.first
end
end
songs.select do |song|
selected.all? { |tag| song.tags.include? tag } and not \
rejected.any? { |tag| song.tags.include? tag }
end
end
def find_by_filter(query)
songs.select(&query)
end
end

Лог от изпълнението

............

Finished in 0.54154 seconds
12 examples, 0 failures

История (1 версия и 1 коментар)

Генади обнови решението на 30.10.2011 13:53 (преди над 12 години)

+class Song
+ attr_accessor :name, :artist, :genre, :subgenre, :tags
+
+ def initialize(format = nil)
+ return if format.nil?
+ @name, @artist, @genre, @subgenre, @tags = parse format
+ end
+
+ def [](key)
+ send(key) rescue nil
+ end
+
+ private
+
+ def parse(format)
+ [].tap do |result|
+ name, artist, genre, tags = format.split('.').map(&:strip)
+ result << name << artist
+ genre.split(',').map(&:strip).tap do |parsed_genre|
+ genre, subgenre = parsed_genre
+ result << genre << subgenre
+ tags = tags.to_s if tags.nil?
+ tags.split(',').map(&:strip).tap do |tags|
+ tags.concat parsed_genre
+ result << tags.map(&:downcase)
+ end
+ end
+ end
+ end
+end
+
+class Collection
+ attr_reader :songs
+
+ def initialize(catalog, additional_tags = {})
+ @songs = catalog.lines.map { |format| Song.new format }
+ additional_tags.each do |artist, tags|
+ find_by_artist(artist).map { |song| song.tags.concat tags }
+ end
+ end
+
+ def find(criteria)
+ return songs if criteria.empty?
+ result = []
+ criteria.each do |field, query|
+ if result.empty?
+ result.concat send("find_by_#{field}", query)
+ else
+ result &= send("find_by_#{field}", query)
+ end
+ end
+ result
+ end
+
+ %w{name artist genre subgenre}.each do |field|
+ define_method("find_by_#{field}") do |query|
+ songs.select { |song| song[field] == query }
+ end
+ end
+
+ def find_by_tags(query)
+ selected, rejected = [].tap do |me|
+ Array(query).tap do |it|
+ me << it.reject { |tag| tag.end_with? '!' }
+ me << it.map { |tag| tag.chomp '!' } - me.first
+ end
+ end
+
+ songs.select do |song|
+ selected.all? { |tag| song.tags.include? tag } and not \
+ rejected.any? { |tag| song.tags.include? tag }
+ end
+ end
+
+ def find_by_filter(query)
+ songs.select(&query)
+ end
+end
  • send(key) rescue nil е ужасно. Това ще погълне всякакви грешки. По-добре send(key) if respond_to? key. Можеш да имаш изключение в някой от тези методи и няма да го видиш.
  • me и it са странни имена на променливи.
  • not rejected.any? е още известно като rejected.none?.
  • Обективно погледнато, нямаш нужда от attr_reader :songs в Collection. Можеш в кода просто да ползваш @songs.
  • Не мога да разбера защо в Song#initialize имаш return if format.nil?. Не се ползва никъде.
  • В Collection#find трябваше да направиш един select, вместо да създаваш масив, да итерираш друг и да правиш concat и &=. Тази логика е доста сложна за проследяване.
  • Интересно, че си открил define_method, но това не е най-удачното ми приложение.
  • find_by_tags е изключително сложен с тези два tap-а и сложнотиите които прави вътре. Щях да ти дам бонус точки, ама това ме разубеди. Можеше вместо всичко това да направиш:

    selected, rejected = Array(query).partition { |tag| tag.end_with? '!' }
    rejected = rejected.map(&:chop)