我有一个列表,我想按字母顺序排序,但要针对Unicode
iex(2)> ["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"] |> Enum.sort
["lubelskie", "mazowieckie", "zachodniopomorskie", "łódzkie"]
# the above is wrong, it should be:
["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
如何在Elixir中做到这一点?可以使用某些十六进制软件包。
答案 0 :(得分:3)
处理排序的正确方法是将所有字符带到decomposed unicode form并进行排序。问题是由于某些原因"ł"
不被认为是组合形式:
letters
|> Enum.map(&:unicode.characters_to_nfd_binary/1)
|> Enum.map(&String.codepoints/1)
#⇒ [
# ["a"],
# ["a", "̨"],
# ["b"],
# ["c"],
# ["c", "́"],
# ["d"],
# ["e"],
# ["e", "̨"],
# ["f"],
# ["g"],
# ["h"],
# ["i"],
# ["j"],
# ["k"],
# ["l"],
# ["ł"],
# ["m"],
# ["n"],
# ["n", "́"],
# ["o"],
# ["o", "́"],
# ["p"],
# ["q"],
# ["r"],
# ["s"],
# ["s", "́"],
# ["t"],
# ["u"],
# ["w"],
# ["y"],
# ["z"],
# ["z", "́"],
# ["z", "̇"]
# ]
我不知道为什么"ł"
不被声明为组合字母,我也认为这是联盟论文中的错误。无论如何,我们可能会愚弄分拣机:
["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
|> Enum.map(&:unicode.characters_to_nfd_binary/1)
|> Enum.map(&String.replace(&1, "ł", "l�"))
|> Enum.sort()
|> Enum.map(&String.replace(&1, "l�", "ł"))
#⇒ ["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
现在,它可以处理合成和分解的任何输入。
答案 1 :(得分:0)
远非完美,但行之有效。
这对我不起作用:
my.exs:
defmodule Stuff do
def numeric_for_sort(string) do
letters = ["a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g", "h", "i", "j", "k", "l", "ł",
"m", "n", "ń", "o", "ó", "p", "q", "r", "s", "ś", "t", "u", "w", "y", "z", "ź", "ż"]
String.graphemes(string)
|> Enum.map(fn(x) -> Enum.find_index(letters, fn(y) -> x == y end) end)
end
end
^C~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Enum.sort(["lubelskie", "mazowieckie", "zachodniopomorskie", "łódzkie"], &(Stuff.numeric_for_sort(&1["name"]) <= Stuff.numeric_for_sort(&2["name"])))
** (FunctionClauseError) no function clause matching in Access.get/3
The following arguments were given to Access.get/3:
# 1
"lubelskie"
# 2
"name"
# 3
nil
(elixir) lib/access.ex:306: Access.get/3
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(stdlib) erl_eval.erl:878: :erl_eval.expr_list/6
(stdlib) erl_eval.erl:404: :erl_eval.expr/5
(stdlib) erl_eval.erl:469: :erl_eval.expr/5
(stdlib) lists.erl:969: :lists.sort/2
(FunctionClauseError) no function clause matching in Access.get/3`.
而且,我不希望您使用字母列表,因为那样一来您就必须不断遍历列表以搜索字母。那就是地图的用途。 (编辑:嗯,我知道的是:small maps是地图上<= 31个条目的有序列表)所以,像这样:
letters = ["a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g", "h", "i", "j", "k", "l", "ł",
"m", "n", "ń", "o", "ó", "p", "q", "r", "s", "ś", "t", "u", "w", "y", "z", "ź", "ż"]
letter_rank = Map.new Enum.with_index letters
String.graphemes(string)
|> Enum.map(fn(x) -> letter_rank[x] end)
然后:
names = ["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
iex(2)> Enum.sort_by names, &Stuff.numeric_for_sort/1
["lubelskie", "łódzkie", "mazowieckie", "zachodniopomorskie"]
iex(3)>
根据Enum.sort_by/3文档:
sort_by / 3与sort / 2的不同之处在于,它仅计算一次可枚举中每个元素的比较值,而不是 每个比较中的每个元素一次。如果相同的功能是 在两个元素上都被调用,使用起来也更紧凑 sort_by / 3。
排序时进行了很多比较,对于排序算法进行的每次比较,一遍又一遍地计算每个名称的数字列表显然不理想。
请注意,即使这一行:
Enum.sort_by names, &Stuff.numeric_for_sort/1
看起来它在调用sort_by / 2,实际上是在使用默认的第三个参数&<=/2
调用sort_by / 3。
答案 2 :(得分:-1)
到目前为止,由于使用的字母定义明确,我最终创建了自己的排序功能:
defp numeric_for_sort(string) do
letters = ["a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g", "h", "i", "j", "k", "l", "ł",
"m", "n", "ń", "o", "ó", "p", "q", "r", "s", "ś", "t", "u", "w", "y", "z", "ź", "ż"]
String.graphemes(string)
|> Enum.map(fn(x) -> Enum.find_index(letters, fn(y) -> x == y end) end)
end
然后
Enum.sort(["lubelskie", "mazowieckie", "zachodniopomorskie", "łódzkie"], &(numeric_for_sort(&1["name"]) <= numeric_for_sort(&2["name"])))
远非完美,但行之有效。