有没有更简单的方法来获取与函数匹配的列表中的第一个项目,然后返回该项目以及没有该项目的列表?

时间:2018-11-04 23:44:58

标签: elixir

示例:我们有一个列表[1,2,3,4]和一个fn &(&1 >= 3)我想找回3[1,2,4]

目前我正在这样做:

index = Enum.find_index(list, func)
elem = Enum.at(list, index)
rest = List.delete_at(list, index)

这是3行代码,看起来像是可以缩短代码长度的通用模式。有没有更好的方法来实现这样的目标?

2 个答案:

答案 0 :(得分:3)

要完成陈述的任务(仅限第一次发生),请使用始终有效的大锤:Enum.reduce_while/3

input = [1, 2, 3, 4]

input
|> Enum.with_index()
|> Enum.reduce_while({nil, []}, fn {e, idx}, {value, rest} ->
  if e >= 3,
      do: {:halt, {e, Enum.reverse(rest) ++ tl(Enum.slice(input, idx..-1))}},
    else: {:cont, {value, [e | rest]}} end)
#⇒ {3, [1, 2, 4]}

此处仅需要with_index技巧用于提高性能。找到该元素后,我们要立即停止迭代,因此需要下一个元素的索引才能将尾部批量添加到结果中。


另一种方法是使用Enum.split_while/2

with {h, [e | t]} <- Enum.split_while(input, fn x -> not(x >= 3) end),
  do: {e, h ++ t}
#⇒ {3, [1, 2, 4]}

答案 1 :(得分:1)

假设完全有一个元素与您的函数匹配,则可以使用split_with +模式匹配:

iex(1)> {[item], rest} = Enum.split_with([1,2,3,4], & &1 == 3)
{[3], [1, 2, 4]}
iex(2)> item
3
iex(3)> rest
[1, 2, 4]

当然,如果列表中有1个以上的匹配项,则会崩溃。如果您只想提取第一个,可以{[item | _], rest},但这仍会从rest中删除所有它们,所以我不确定这是否就是您想要的。