如何截断elixir中的字符串?

asi*_*niy 10 elixir

我正在使用slu for elixir,我的想法是:我有一个字符串,[a-zA-Z0-9]用连字符分隔.喜欢:

string = "another-long-string-to-be-truncated-and-much-text-here"
Run Code Online (Sandbox Code Playgroud)

我想确保最大字符串长度等于30,但我也希望确保在达到最大长度时单词不会减半.所以前30个符号string是,another-long-string-to-be-trun但我希望有完全删除的another-long-string-to-be单词truncated.我怎样才能做到这一点?

Yur*_*kov 6

首先,如果您根本不关心性能,则可以将所有工作传递给正则表达式:

~r/\A(.{0,30})(?:-|\Z)/

我认为这将是最短的解决方案,但效率不高:

iex(28)> string
"another-long-string-to-be-truncated-and-much-text-here"
iex(29)> string2
"another-long-string-to-be-cool-about-that"

iex(30)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string) |> List.last() 
"another-long-string-to-be"

iex(31)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string2) |> List.last()
"another-long-string-to-be-cool"
Run Code Online (Sandbox Code Playgroud)

高效的解决方案

但是,如果您确实关心性能和内存,那么我建议这样做:

defmodule CoolSlugHelper do
  def slug(input, length \\ 30) do
    length_minus_1 = length - 1

    case input do
      # if the substring ends with "-"
      # i. e. "abc-def-ghi", 8 or "abc-def-", 8 -> "abc-def"
      <<result::binary-size(length_minus_1), "-", _::binary>> -> result

      # if the next char after the substring is "-"
      # i. e. "abc-def-ghi", 7 or "abc-def-", 7 -> "abc-def"
      <<result::binary-size(length), "-", _::binary>> -> result

      # if it is the exact string. i. e. "abc-def", 7 -> "abc-def"
      <<_::binary-size(length)>> -> input

      # return an empty string if we reached the beginnig of the string
      _ when length <= 1 -> ""

      # otherwise look into shorter substring
      _ -> slug(input, length_minus_1)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

它不会按字符收集结果字符串。而是从所需长度到1的范围内寻找正确的子字符串。这就是它在内存和速度方面变得有效的方式。

我们需要此length_minus_1变量,因为我们不能在binary-size二进制模式匹配中使用表达式。

这是截至2018年12月22日所有拟议解决方案的基准:

(简单的~r/\A(.{0,30})(?:-|\Z)/正则表达式就是上面的正则表达式)

Name                     ips        average  deviation         median         99th %
CoolSlugHelper      352.14 K        2.84 ?s  ±1184.93%           2 ?s           8 ?s
SlugHelper           70.98 K       14.09 ?s   ±170.20%          10 ?s          87 ?s
Simple Regex         33.14 K       30.17 ?s   ±942.90%          21 ?s         126 ?s
Truncation           11.56 K       86.51 ?s    ±84.81%          62 ?s         299 ?s

Comparison: 
CoolSlugHelper      352.14 K
SlugHelper           70.98 K - 4.96x slower
Simple Regex         33.14 K - 10.63x slower
Truncation           11.56 K - 30.46x slower

Memory usage statistics:

Name              Memory usage
CoolSlugHelper         2.30 KB
SlugHelper            12.94 KB - 5.61x memory usage
Simple Regex          20.16 KB - 8.75x memory usage
Truncation            35.36 KB - 15.34x memory usage
Run Code Online (Sandbox Code Playgroud)


Ale*_*kin 5

UPD 12/2018 Yuri Golobokov在这里发布了更好的解决方案,我建议在下面使用它。


最简单的方法是:

"another-long-string-to-be-truncated-and-much-text-here"
|> String.slice(0..29) 
|> String.replace(~r{-[^-]*$}, "")
#? "another-long-string-to-be"
Run Code Online (Sandbox Code Playgroud)

有一个小故障:如果连字符恰好在第31位,则最后一项将被删除。为了避免这种情况,可以明确检查上述情况:

str = "another-long-string-to-be-truncated-and-much-text-here"
case str |> String.at(30) do                                      
  "-" -> str |> String.slice(0..29)                                  
  _   -> str |> String.slice(0..29) |> String.replace(~r{-[^-]*$}, "")
end                                                               
#? "another-long-string-to-be"
Run Code Online (Sandbox Code Playgroud)

要么:

orig = "another-long-string-to-be-cool-cated-and-much-text-here"
str = orig |> String.slice(0..29) 
unless String.at(orig, 30) == "-", do: str = str |> String.replace(~r{-[^-]*$}, "")
str
#? "another-long-string-to-be-cool"
Run Code Online (Sandbox Code Playgroud)