Sorting a list alphabetic then numeric

Scenario
I have a list I’m getting back where I have a mixture of letters and numbers as strings. I need the list to be sorted so it’s alphabetic first THEN numeric.

``````# Sample data

[
{"1",[]},
{"A",[]},
{"B",[]},
{"C",[]},
{"D",[]},
{"E",[]},
{"F",[]},
{"3",[]},
{"4",[]},
{"5",[]},
{"6",[]}
]

# But I want this as the final result

[
{"A", []},
{"B", []},
{"C", []},
{"D", []},
{"E", []},
{"F", []},
{"1", []},
{"3", []},
{"4", []}
]

``````

My solution works
This is what I came up with and it seems to work

``````defmodule Foo do

@list [
{"1",[]},
{"A",[]},
{"B",[]},
{"C",[]},
{"D",[]},
{"E",[]},
{"F",[]},
{"3",[]},
{"4",[]},
{"5",[]},
{"6",[]}
]

def list do
@list
end

def sort_alpha_then_numeric(list) do

numbers_alpabetic_list =
Enum.split_with(list, fn item ->
{key, _value} = item
case Integer.parse(key) do
{_, ""} -> true
:error -> false
end
end)

{number_list, alpha_list} = numbers_alpabetic_list

# We now switch it so that the order is alpabetic then numeric
List.flatten(alpha_list, number_list)

end
end
``````

So if I try this out

``````Foo.sort_alpha_then_numeric Foo.list
# returns
[
{"A", []},
{"B", []},
{"C", []},
{"D", []},
{"E", []},
{"F", []},
{"1", []},
{"3", []},
{"4", []}
]
``````

Question
It works, but would like to know if my approach can be improved and learn something about sorting. Thanks.

1 Like

Take a look at `Enum.sort_by`.

Using function proposed by @LostKobrakai:

``````def sort(list), do: Enum.sort_by(list, &comparator/2)

defp comparator(<<a, _::binary>> = ax, <<b, _::binary>> = bx) when a in ?0..?9 and b in ?0..9, do: ax <= bx
defp comparator(<<a, _::binary>>, _) when a in ?0..?9, do: true
defp comparator(ax, bx), do: ax <= bx
``````

However it is not clear how `"0a"` should compare with `"01"`. I assumed that only first character decides. Otherwise you need to expand `comparator/2` function a little.

EDIT: Fixed snippet, the `in` operator was missing.

1 Like

I’m getting this error when I copy and paste this into my `Foo` module. It looks like

``````missing :do option in "defp"
``````

Also this syntax is foreign to me. Is this just saying that ‘b’ is expected to be a binary type?

``````<<b, _::binary>>
``````

No, this is pattern matching on binaries. We are getting out first byte and then ignoring the rest `_` has type binary. More info `Kernel.SpecialForms.<<>>`

1 Like