# Getting adjacent values from a list

Hello

What would be the best way to get adjacent values for given value inside a list. Something like:

``````list = [7, 3, 8, 1, 5, 9]
val = 1
[head | val | tail] = list
``````

And now in the `head` I can check the `last` element (that would be 8), and in the `tail` I can check the `first` element (that would be 5). But that doesn’t work Any other “clever” method to get that values?

With no real check, get first matching result, or last invalid…

``````val = 1
Enum.reduce_while(list, {nil, nil, nil}, fn x, acc ->
new_acc = {elem(acc, 1), elem(acc, 2), x}
if elem(new_acc, 1) != val, do: {:cont, new_acc}, else: {:halt, new_acc}
end)
{8, 1, 5}
``````

Try

``````  def adj([], value), do: nil

def adj([h | t], value) do
end
``````

Another solution may be Enum.split_while/2

``````list = [7, 3, 8, 1, 5, 9]
val = 1
# at 1 because enumerables are 0-based
list |> Enum.split_while(fn elem -> elem != val end) |> elem(1) |> Enum.at(1)

# It's not as fast for previous element, but still easy to get:
list |> Enum.split_while(fn elem -> elem != val end) |> elem(0) |> Enum.at(-1)
list |> Enum.split_while(fn elem -> elem != val end) |> elem(0) |> List.last()
``````
1 Like

Thank you for all the answers! But I think in the end I will go with:

``````list = [7, 3, 8, 1, 5, 9]
val = 9

index = Enum.find_index(list, fn e -> e == val end)
case index do
0 ->
{nil, Enum.at(list, index + 1)}
_ ->
{Enum.at(list, index - 1), Enum.at(list, index + 1)}
end
``````

Mainly because it is more readable to me.

The recursive function above finds the right adjacent value.

This one below finds the 2 adjacent:

``````  def adjs([], _), do: []
def adjs([val, right | t], value) when val == value, do: right
def adjs([left, val, right | t], value) when val == value, do: {left, right}

def adjs([_h | t], value) do
end
``````
3 Likes

This seems the best solution here if you count time, but in my case, there is db query anyway, so I don’t care that much. But this is the solution that I will go when I would consider time!

Okay.
Just to be stricter and uniform the results,
`def adjs([], _), do: []` can be changed to `def adjs([], _), do: nil`

so the spec would be
`@spec adjs(list, integer) :: nil | integer | tuple`

These two clauses can be simplified (no need for guards)

``````  def adjs([value, right | t], value), do: right
def adjs([left, value, right | t], value), do: {left, right}
``````
1 Like

Also, keep in mind to return in the format of` {:ok, value}`,` {:ok, value1, value2}`, `:error`
otherwise if you just get `{value1, value2}` or `[]` you will never be sure if you are getting two values, or the one value you got is a two-element tuple.

Here there is no possible error. Max, it returns nil.
Also, the return value spec being either nil, or an integer or a tuple allows, via pattern matching, to distinguish the different cases.
One could do

``````adjs(list)
|> case  do
nil -> :foo
val when is_integer(val) -> :bar
{left, right} -> :baz
end
``````

Well, if the list only contians integers, yes. But it is never mentioned that. It talks about value, so I expect a tuple, nil, or and empty list could be a value as well.