#TIL #elixirlang 1.13.0 이전 elixir에는 없는 map 필터 함수

map = for i <- 1..100, into: %{}, do: {i, Enum.random([false, true])}

키는 숫자, 값은 boolean인 map을 만든다. %{1 => true, 2 => false} 이런 식으로 만들어진 map이다.

value가 true인 key, value 쌍을 뽑아서 map을 만들려면 어떻게 하면 될까? Enum.filter/2 함수를 써서 value가 true인 key, value 쌍을 걸러내 보자.

map |> Enum.filter(&match?({_, true}, &1))
[
  {12, true},
  {38, true},
  {93, true},
  {53, true},
  {46, true},
  {23, true},
  {80, true},
  {96, true},
  {75, ...},
  {...},
  ...
]

Enum.filter/2 리턴 값이 list라서 map이 아닌 list가 나온다.

map |> Enum.filter(&match?({_, true}, &1)) |> Map.new()
%{
  48 => true,
  62 => true,
  39 => true,
  83 => true,
  63 => true,
  34 => true,
  68 => true,
  ...
}

map에 filter를 적용하려면 list를 리턴하는 Enum.filter/2 함수 다음에 Map.new/1 함수를 사용해 list를 map으로 만들어야 한다.

erlang도 이럴까? erlang 라이브러리에는 둘러가지 않고 한방에 가는 maps.filter/2 함수가 있다.

:maps.filter(fn _, value -> value end, map)
%{
  48 => true,
  62 => true,
  39 => true,
  83 => true,
  63 => true,
  34 => true,
  ...
}

pipe 연산자에 친화적이지 않다. map을 첫 번째 인자로 받지 않아 마음에 안 들지만 잘 동작한다. 중간에 list를 생성하지 않아 훨씬 더 빠르다.

다행히 elixir 1.13.0에 추가된다. Map.filter/2 함수뿐만 아니라 Map.map/2 함수도 추가된다.

버전업이 곤란하면 구현 함수를 복사해서 사용하면 된다. 단순 :maps.filter/2 함수 호출인가 싶어서 코드를 살펴봤다. :maps.iterator/1, :maps.next/1 함수로 반복자(iterator)를 만들어서 요소들을 차례로 순회하며 filter 함수를 호출한다. 생각해보니 map 요소에 접근하는 함수 시그니처(signature)도 다르다. elixir에서는 tuple을 인자로 받는데, erlang에선 2개의 인자를 받는다. 그래서 직접 순회하며 filter 함수를 호출하게 했다.

참고

Feedback plz <3 @ohyecloudy, ohyecloudy@gmail.com

A Random Post