doc_count = [
["foo", "2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
"2019-02-01", "2019-03-01", "2019-04-01"],
["bar", "2019-01-01: 8876", "2019-04-01: 8694", "2019-01-01",
"2019-02-01", "2019-03-01", "2019-04-01"]
]
Мы можем написать
def doit(doc_count)
doc_count.map do |arr|
date_strings, other_strings =
arr.partition { |s| s.match? /\A\d{4}-\d{2}-\d{2}(?::|\z)/ }
other_strings + select_dates(date_strings)
end
end
, где select_dates
- метод, который еще предстоит построить.
Вычисления для doc_count[0]
следующие:
arr = doc_count[0]
#=> ["foo", "2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
# "2019-02-01", "2019-03-01", "2019-04-01"]
date_strings, other_strings =
arr.partition { |s| s.match? /\A\d{4}-\d{2}-\d{2}(?::|\z)/ }
#=> [["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
# "2019-02-01", "2019-03-01", "2019-04-01"], ["foo"]]
date_strings
#=> ["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
# "2019-02-01", "2019-03-01", "2019-04-01"]
other_strings
#=> ["foo"]
Расчеты для второго элемента doc_count
аналогичны. См. Перечислимый # раздел .
Теперь я дам два способа построения метода select_dates
, первый из которых более эффективен, а второй, возможно, более прост.
Используйте форму Hash # update (он же merge!
), который использует блок для определения значений ключей, которые присутствуют в обоих объединяемых хэшах
def select_dates(date_strings)
date_strings.each_with_object({}) do |s,h|
h.update(s[0, 10]=>s) { |_,o,n| n.size >= o.size ? n : o }
end.values
end
См. Объяснения do c переменных блока _
, o
и n
(_
- допустимая локальная переменная - используется для первой переменной блока, чтобы сообщить читателю, что она не используется в расчете блока). Для date_strings
, указанного выше для doc_count[0]
select_dates(date_strings)
#=> ["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
# "2019-04-01"]
Расчеты следующие.
enum = date_strings.each_with_object({})
#=> #<Enumerator: ["2019-02-01: 186904", "2019-03-01: 196961",
# "2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01"
# ]:each_with_object({})>
s,h = enum.next
#=> ["2019-02-01: 186904", {}]
s #=> "2019-02-01: 186904"
h #=> {}
key = s[0, 10]
#=> "2019-02-01"
h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
#=> {"2019-02-01"=>"2019-02-01: 186904"}
s,h = enum.next
#=> ["2019-03-01: 196961", {"2019-02-01"=>"2019-02-01: 186904"}]
key = s[0, 10]
#=> "2019-03-01"
h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
#=> {"2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961"}
s,h = enum.next
#=> ["2019-01-01", {"2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961"}]
key = s[0, 10]
#=> "2019-01-01"
h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
#=> {"2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"}
s,h = enum.next
#=> ["2019-02-01", {"2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"}]
key = s[0, 10]
#=> "2019-02-01"
h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
#=> {"2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"}
Для первых трех элементов enum
, которые генерируются и передаются в блок, блок update
не вступает в игру, так как объединяемые два хэша (h
и { key=>s }
) не имеют общего ключа. Для четвертого элемента ("2019-02-01")
, который присутствует в обоих объединяемых хэшах, мы отсылаем к блоку для сравнения h["2019-02-01"].size #=> "2019-02-01: 186904".size => 18
с "2019-02-01".size #=> 10
. Поскольку первое больше, мы сохраняем его как значение "2019-02-01"
в h
. Остальные расчеты для update
аналогичны, в результате чего:
h #=> ["2019-02-01"=>"2019-02-01: 186904",
# "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01",
# "2019-04-01"=>"2019-04-01" }
Последний шаг заключается в извлечении значений из этого га sh (h.values
).
Используйте Array # uniq
def select_dates(date_strings)
date_strings.sort_by(&:size).reverse.uniq { |s| s[0, 10] }
end
Для date_strings
, приведенного выше для doc_count[0]
select_dates(date_strings)
#=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
# "2019-01-01"]
Вычисления следующие.
a = date_strings.sort_by(&:size)
#=> ["2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01",
# "2019-02-01: 186904", "2019-03-01: 196961"]
b = a.reverse
#=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
# "2019-03-01", "2019-02-01", "2019-01-01"]
b.uniq { |s| s[0, 10] }
#=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
# "2019-01-01"]
Обратите внимание, что do c для Array#uniq
говорит о том, что «self
пройден по порядку, и первое вхождение сохраняется». Выражение
sort_by(&:size).reverse
можно заменить на
sort_by { |s| -s.size }
, но сообщалось, что то, что я использовал, имеет тенденцию быть быстрее.