person.
slice_before { |_,v| v.is_a?(String) &&
v.match?(/\Apool_\p{Lower}{,2}\z/) }.
each_with_object({}) do |((_, pool), *rest),h|
pool_str = pool + ".member_pool_"
rest.group_by { |k,_| k[/(?:\.\d+)+\z/] }.
each do |_,((_,s),(_,n))|
(s,n = n,s) if n.is_a?(String)
h[(pool_str + s[/[^_]+\z/]).to_sym] = n
end
end
#=> {:"pool_a.member_pool_a1"=>0,
# :"pool_b.member_pool_b1"=>2,
# :"pool_b.member_pool_b2"=>3,
# :"pool_c.member_pool_c1"=>11,
# :"pool_c.member_pool_c2"=>22,
# :"pool_c.member_pool_c3"=>33}
Шаги следующие: 1 .
c = person.slice_before { |_,v| v.is_a?(String) &&
v.match?(/\Apool_\p{Lower}{,2}\z/) }
#=> #<Enumerator: #<Enumerator::Generator:0x00005d500f652950>:each>
Мы можем увидеть значения, которые будут генерироваться этим перечислителем, преобразовав его в массив.
c.to_a
#=> [[[:"PoolName.11.22.33", "pool_a"],
# [:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]],
# [[:"PoolName.11.22.44", "pool_b"],
# [:"PoolMemberName.11.22.44.11", "member_pool_b1"],
# [:"PoolMemberName.11.22.44.12", "member_pool_b2"],
# [:"PoolMemberScore.11.22.44.11", 2],
# [:"PoolMemberScore.11.22.44.12", 3]],
# [[:"PoolName.11.22.55", "pool_c"],
# [:"PoolMemberName.11.22.55.11", "member_pool_c1"],
# [:"PolMemberName.11.22.55.12", "member_pool_c2"],
# [:"PoolMemberName.11.22.55.13", "member_pool_c3"],
# [:"PoolMemberScore.11.22.55.11", 11],
# [:"PoolMemberScore.11.22.55.12", 22],
# [:"PoolMemberScore.11.22.55.13", 33]]]
Как видно, c
генерирует 3 массива, по одному для каждого из трех пулов, "a", "b" и "c".См. Enumerable # slice_before .Продолжая,
d = c.each_with_object({})
#=> #<Enumerator: #<Enumerator:
# #<Enumerator::Generator:0x00005d500f652950>:each>
# :each_with_object({})>
d
можно рассматривать как составной перечислитель , хотя у Ruby такого понятия нет.См. Enumerable # each_with_object .Первый элемент генерируется и передается в блок, а переменные блока присваиваются этому значению.
((_, pool), *rest),h = d.next
#=> [[[:"PoolName.11.22.33", "pool_a"],
# [:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]], {}]
Мы видим, что переменные блока теперь имеют следующие значения:
pool
#=> "pool_a"
rest
#=> [[:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]]
h #=> {}
Разбиение массивов на интересующие их компоненты называется «декомпозиция массива» (поиск "Разложение массива").Он также известен как неоднозначность массива .Это очень мощная и полезная техника.Обратите внимание, что _
является допустимой, но несколько особенной локальной переменной.Основная причина использования подчеркивания (или имени переменной, начинающегося с подчеркивания) заключается в информировании читателя о том, что эта переменная не используется в последующих вычислениях.Продолжая,
pool_str = pool + ".member_pool_"
#=> "pool_a.member_pool_"
e = rest.group_by { |k,_| k[/(?:\.\d+)+\z/] }
#=> {".11.22.33.11"=>[
# [:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]]}
См. Enumerable # group_by .Продолжая,
g = e.each
#=> #<Enumerator:
# {".11.22.33.11"=>[
# [:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]]}:each>
Много счетчиков, а?Опять же, первый элемент перечислителя генерируется и передается в блок, а переменным блока присваиваются значения:
p,((q,s),(r,n)) = g.next
#=> [".11.22.33.11",
# [[:"PoolMemberName.11.22.33.11", "member_pool_a1"],
# [:"PoolMemberScore.11.22.33.11", 0]]]
p #=> ".11.22.33.11"
q #=> :"PoolMemberName.11.22.33.11"
s #=> "member_pool_a1"
r #=> :"PoolMemberScore.11.22.33.11"
n #=> 0
p
, q
и r
не используются в вычислениях блока,По этой причине я использовал _
для всех трех из этих переменных:
each do |_,((_,s),(_,n))| ...
Эта декомпозиция массива может выглядеть чрезвычайно сложной, но на самом деле это не так.Просто сравните расположение скобок в строке, приведенной выше, с расположением скобок в возвращаемом значении g.next
, показанном выше.
Теперь выполняется вычисление блока.Мы не можем быть уверены, что два элемента g.next
находятся в правильном порядке (хотя здесь они есть), поэтому мы обращаем их вспять при необходимости.
(s,n = n,s) if n.is_a?(String)
#=> nil
s #=> "member_pool_a1"
n #=> 0
i = s[/[^_]+\z/]
#=> "a1"
j = pool_str + i
#=> "pool_a.member_pool_a1"
k = j.to_sym
#=> :"pool_a.member_pool_a1"
h[k] = n
#=> 0
h #=> {:"pool_a.member_pool_a1"=>0}
Поскольку g.size #=> 1
мы закончили с g
,Поэтому следующим шагом является генерация следующего элемента d
(пул "b"):
((_, pool), *rest),h = d.next
#=> [[[:"PoolName.11.22.44", "pool_b"],
# [:"PoolMemberName.11.22.44.11", "member_pool_b1"],
# [:"PoolMemberName.11.22.44.12", "member_pool_b2"],
# [:"PoolMemberScore.11.22.44.11", 2],
# [:"PoolMemberScore.11.22.44.12", 3]],
# {:"pool_a.member_pool_a1"=>0}]
pool
#=> "pool_b"
rest
#=> [[:"PoolMemberName.11.22.44.11", "member_pool_b1"],
# [:"PoolMemberName.11.22.44.12", "member_pool_b2"],
# [:"PoolMemberScore.11.22.44.11", 2],
# [:"PoolMemberScore.11.22.44.12", 3]]
h #=> {:"pool_a.member_pool_a1"=>0}
Обратите внимание, как была изменена блочная переменная h
.Остальные расчеты аналогичны.
1 Опытные рубины: подробное предупреждение!