Я использовал левый и правый указатели (lt_ptr
и rt_ptr
), чтобы построить две части строки, которые должны быть сохранены и разделены многоточием.
Я начинаю с уменьшенияправый указатель на последнюю косую черту, затем увеличьте левый указатель до первой косой черты, затем уменьшите правый указатель до ближайшей косой черты и так далее, пока дальнейшие перемещения указателя не сделают строку, содержащую многоточиедлиннее максимально допустимой длины.
Код
def shorten_string(str, max_len, ellipsis = '...')
return str if str.size <= max_len
max_len -= ellipsis.size
ops = [{ index: :index, ptr: 0, chg: 1 },
{ index: :rindex, ptr: str.size-1, chg: -1 } ].cycle
op = ops.next
success = true
loop do
op = ops.next
ptr = str.public_send(op[:index], '/', op[:ptr] + op[:chg] )
lptr = ptr
rptr = ops.peek[:ptr]
lptr, rptr = rptr, lptr if op[:index] == :rindex
if lptr + 1 + str.size - rptr <= max_len
op[:ptr] = ptr
else
break unless success
success = false
end
end
op = ops.next if op[:index] == :rindex
"%s%s%s" % [str[0..op[:ptr]], ellipsis, str[ops.peek[:ptr]..-1]]
end
Примеры
str = 'a/very/long/path/with/too/many/characters/to/fit/on/my/screen/easily'
shorten_string(str, 40)
#=> "a/very/long/path/.../on/my/screen/easily" (length: 40)
shorten_string(str, 30)
#=> "a/very/.../on/my/screen/easily" (length: 30)
shorten_string(str, 20)
#=> "a/.../screen/easily" (length: 19)
Это, конечно, совпадение, что в первых двух примерах длина результирующей строки (с многоточием) была точно равна максимальной длине, max_length
.Обратите внимание, что во втором примере после многоточия есть 4 косых черты и только 2 перед.Это связано с тем, что после добавления "/my"
можно было добавить только 3 символа, недостаточно для "long/"
(что следует за "very/"
), но достаточно для "/on"
.
Объяснение
Используются формы String # index и [String # rindex] (http://ruby -doc.org / core- 2.5.1 / String.html# method-i-rindex), в котором используется второй аргумент.
Чтобы лучше понять выполняемые вычисления, я предлагаю запустить код для примера, после того как код будет добавлен в операторы puts
.Ниже приведен пример модифицированного метода.
def shorten_string(str, max_len, ellipsis = '...')
return str if str.size <= max_len
max_len -= ellipsis.size
puts "str.size=#{str.size}" #!!!!
ops = [{ index: :index, ptr: 0, chg: 1 },
{ index: :rindex, ptr: str.size-1, chg: -1 } ].cycle
op = ops.next
success = true
loop do
op = ops.next
puts "\nop=#{op}, ops.peek=#{ops.peek}" #!!!!
ptr = str.public_send(op[:index], '/', op[:ptr] + op[:chg] )
lptr = ptr
rptr = ops.peek[:ptr]
puts "ptr=#{ptr}, lptr=#{lptr}, rptr=#{rptr}" #!!!!
lptr, rptr = rptr, lptr if op[:index] == :rindex
puts "after possible flip, lptr=#{lptr}, rptr=#{rptr}" #!!!!
puts "lptr + 1 + str.size - rptr = #{lptr+1+str.size-rptr}" #!!!!
if lptr + 1 + str.size - rptr <= max_len
op[:ptr] = ptr
puts "after lptr+1+str.size-rptr <= max_len, op=#{op}" #!!!!
else
break unless success
success = false
end
end
puts "after loop op=#{op}, ops.peek=#{ops.peek}" #!!!!
op = ops.next if op[:index] == :rindex
"%s%s%s" % [str[0..op[:ptr]], ellipsis, str[ops.peek[:ptr]..-1]]
end