sarat1669
How to swap elements in a list?
Is there a better way to swap elements in a list?
Any inbuilt function or a library?
defmodule SwapElements do
def swap(list, first_index, second_index) do
{a, b} = split(list, first_index + 1)
{x, y} = split(b, second_index - first_index)
[h1 | t1] = a
[h2 | t2] = x
y
|> reverse_append([h1])
|> reverse_append(t2)
|> reverse_append([h2])
|> reverse_append(t1)
end
def split(list, n) do
split([], list, n)
end
def split(a, b, 0) do
{a, b}
end
def split(list, [h|t], n) do
split([h | list], t, n - 1)
end
def reverse_append(list, []) do
list
end
def reverse_append(list, [h | t]) do
reverse_append([h | list], t)
end
end
iex(27)> SwapElements.swap(Enum.to_list(0..10), 1, 4)
[0, 4, 2, 3, 1, 5, 6, 7, 8, 9, 10]
Most Liked
al2o3cr
I don’t know when this would ever be useful, but here’s a version of swap that even works on infinite streams!
defmodule Swap3 do
def swap(a, i1, i2) do
a
|> Stream.with_index()
|> Stream.transform(:start, &do_swap(&1, &2, i1, i2))
end
defp do_swap({el, idx}, :start, i1, _) when idx < i1 do
{[el], :start}
end
defp do_swap({el, idx}, :start, i1, _) when idx == i1 do
{[], {el, []}}
end
defp do_swap({el, idx}, {first_el, acc}, _, i2) when idx < i2 do
{[], {first_el, [el | acc]}}
end
defp do_swap({second_el, idx}, {first_el, acc}, _, i2) when idx == i2 do
result = Stream.concat([[second_el], Enum.reverse(acc), [first_el]])
{result, :end}
end
defp do_swap({el, _}, :end, _, _) do
{[el], :end}
end
end
This uses Stream.transform with a reducer function that implements a tiny state machine to handle the change in behavior when the two indexes are passed.
It also chains:
Stream.iterate(0, &(&1 + 1))
|> Swap3.swap(5, 12)
|> Swap3.swap(2, 18)
|> Swap3.swap(7, 13)
|> Stream.take(20)
|> Enum.to_list()
# gives
[0, 1, 18, 3, 4, 12, 6, 13, 8, 9, 10, 11, 5, 7, 14, 15, 16, 17, 2, 19]
While it’s a streaming algorithm, it still needs to hold at least i2-i1 intermediate elements in memory since it can’t produce the i1th element until it’s seen the i2th.
Also beware: Swap3.swap does weird things if the supplied indexes aren’t in order (i1 < i2) or are equal.
al2o3cr
Here are two possible approaches using functions from Enum and List:
defmodule Swap do
def swap(a, i1, i2) do
{first, [e1 | middle]} = Enum.split(a, i1)
{middle, [e2 | rest]} = Enum.split(middle, i2-i1-1)
List.flatten([first, e2, middle, e1, rest])
end
end
defmodule Swap2 do
def swap(a, i1, i2) do
e1 = Enum.at(a, i1)
e2 = Enum.at(a, i2)
a
|> List.replace_at(i1, e2)
|> List.replace_at(i2, e1)
end
end
Beware that both of these (just like the one in your post) have O(N) time-complexity since they have to traverse the entire list.
benwilson512
Swap2 is definitely the most clear imho, nice stuff.
@sarat1669 It’s probably worth noting that if you want to perform a bunch of index based changes to a “list” you are probably better off using a map with the indices as keys rather than a list, which just really isn’t setup for efficient index based access or changes.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








