Код
def doit(arr, min_common_length)
arr.map do |label, *values|
[label, values.group_by { |s| s[0, min_common_length] }.
map { |_,a| a.first[0, nbr_common_digits(a, min_common_length)] }]
end
end
def nbr_common_digits(a, min_common_length)
max_digits = a.map(&:size).min
return max_digits if max_digits == min_common_length + 1
(min_common_length..max_digits).find { |i|
a.map { |s| s[i] }.uniq.size > 1 } || max_digits
end
Пример
arr = [["A","2491250873330","249111222333","2491250872214","2491250872213"],
["B","221709900000"],
["C","6590247968","6590247969","6598540040","65985400217"]]
doit(arr, 8)
#=> [["A", ["249125087", "249111222333"]],
# ["B", ["221709900000"]],
# ["C", ["659024796", "65985400"]]]
Объяснение
Давайте сначаларассмотрим вспомогательный метод, nbr_common_digits
.Предположим, что
a = ["123467", "12345", "1234789"]
min_common_length = 2
, тогда шаги следующие:
max_digits = a.map(&:size).min
#=> 5 (the length of "12345")
max_digits == min_common_length + 1
#=> 5 == 2 + 1
#=> false, so do not return max_digits
b = (min_common_length..max_digits).find { |i| a.map { |s| s[i] }.uniq.size > 1 }
#=> (2..5).find { |i| a.map { |s| s[i] }.uniq.size > 1 }
#=> 4
На этом этапе мы должны рассмотреть возможность того, что b
будет равно nil
, что происходит, когда первый 5
символов всех строк равны.В этом случае мы должны вернуть max_digits
, поэтому нам требуется следующее.
b || max_digits
#=> 4
В doit
шаги следующие:
min_common_length = 8
Во-первых, мы используем Enumerable # group_by для группировки значений по их первым min_common_length
цифрам.
arr.map { |label, *values| [label,
values.group_by { |s| s[0, min_common_length] }] }
#=> [["A", {"24912508"=>["2491250873330", "2491250872214", "2491250872213"],
# "24911122"=>["249111222333"]}],
# ["B", {"22170990"=>["221709900000"]}],
# ["C", {"65902479"=>["6590247968", "6590247969"],
# "65985400"=>["6598540040", "65985400217"]}]]
Второй шаг - вычисление самых длинных общих длин и замена значений при необходимости.
arr.map do |label, *values| [label,
values.group_by { |s| s[0, min_common_length] }.
map { |_,a| a.first[0, nbr_common_digits(a, min_common_length)] }]
end
#=> [["A", ["249125087", "249111222333"]],
# ["B", ["221709900000"]],
# ["C", ["659024796", "65985400"]]]
Первая переменная блока во втором блоке map
(значение которого равно строке с nbr_common_length
символами - критерий группировки group_by
) представлена подчеркиванием (допустимой локальной переменной) дляозначает, что он не используется в расчете блока.