I was trying my hand at learning Elixir via ancient adventofcode puzzles and was trying to write a function to satisfy this requirement:

It contains a pair of any two letters that appears at least twice in the string without overlapping, like xyxy (xy) or aabcdefgaa (aa), but not like aaa (aa, but it overlaps).

Here is what I came up with, but it feels like it might be unwieldy. Is there a way to make it more elegant?

  def has_non_overlapping_pair(string) do
      |> Enum.map(fn n ->
          String.slice(string, n, 2)
      |> Enum.reduce(["_"], fn pair, acc ->
          if pair == hd(acc) do ["_"|acc] 
          else [pair|acc] end
      |> count_occurrences
      |> Map.delete("_")
      |> Map.values
      |> Enum.any?(fn val -> val > 1 end)

You could use a map to store the pairs with their first occurrence while iterating over the pairs. Break if the current pair was already found at a position less than the current position minus 1.

def has_non_overlapping_pair(string) do
  0..String.length(string) - 2
  |> Enum.reduce_while(%{}, fn n, m ->
    pair = String.slice(string, n, 2)
    pos  = Map.get(m, pair, n)
    cond do
      pos == n     -> {:cont, Map.put(m, pair, n)}
      pos == n - 1 -> {:cont, m}
      true         -> {:halt, true}
  end) == true
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.