Question

How can I remove nils from this list ,lets say i get:

[{"some","other",[]},nil,{{"more","somemore",[]},nil,nil}]

In the end I would like to extract only the first elements from the long tuples and put them on a list, Something like:

["some", "more"]

Was it helpful?

Solution

You can remove nils from the list using function like that:

filter_out_nils(Data) when is_list(Data) ->
    Pred = fun(Element) -> Element /= nil end,
    lists:filter(Pred, Data). 

This function does not remove nils within tuples though.

And you can use a couple of functions to extract every first non tuple element in you list (like strings "some" and more):

extract_first_elements(Data) when is_list(Data) ->
    lists:map(fun extract_first_non_tuple_element/1, Data).

extract_first_non_tuple_element({})-> {};
extract_first_non_tuple_element(Data) when is_tuple(Data)-> 
    case element(1, Data) of
        First when is_tuple(First) -> extract_first_non_tuple_element(First);
        Other -> Other
    end.

Function extract_first_non_tuple_element is recursive, because in your example tuple can be nested.

So to test this functions:

Data1 = [{"some","other",[]}, nil, {{"more","somemore",[]}, nil, nil}].
filter_out_nils(Data1).
[{"some","other",[]},{{"more","somemore",[]},nil,nil}] % resulting list without nils
Data2 = extract_first_elements(Data1).
["some","more"] % extracted first elements

Update. To remove nils from nested tuples we can use function like that:

filter_out_nils_from_tuple(Data) when is_tuple(Data) ->
    TList = tuple_to_list(Data),
    Fun = fun(Element, Acc) ->
        case Element of
            nil -> Acc;
            Tuple when is_tuple(Tuple) -> Acc ++ [filter_out_nils_from_tuple(Tuple)];
            Other -> Acc ++ [Other]
        end
    end,
    Result = lists:foldl(Fun, [], TList),
    list_to_tuple(Result).

OTHER TIPS

Filtering out nils and getting the first element of nested tuples in your example can be achieved with a single recursive function, with a clause for the nil case:

f([Item  | T], Acc) when is_tuple(Item) -> f([element(1, Item) | T], Acc);
f([nil   | T], Acc) -> f(T, Acc); % filter out nil
f([Other | T], Acc) -> f(T, [Other | Acc]);
f([], Acc) -> lists:reverse(Acc).

Since you added erlang-shell tag, please note this solution will not work directly in the shell. Indeed, recursive functions in the shell shall be written as functions taking a function (themselves) as an argument (cf: How do you write a fun that's recursive in Erlang?).

F = fun(F, [Item  | T], Acc) when is_tuple(Item) ->
            F(F, [element(1, Item) | T], Acc);
       (F, [nil   | T], Acc) -> F(F, T, Acc);
       (F, [Other | T], Acc) -> F(F, T, [Other | Acc]);
       (_F, [], Acc) -> lists:reverse(Acc)
    end.
F(F, List, []).

Please also note that this solution has specific behaviors for cases not covered in your question:

  1. It will crash with a function clause error if the input list contains an empty tuple. Yet, this might be a desired behavior. Otherwise, you can simply add a new function clause to handle it as desired (should empty tuples be filtered out or returned?).

  2. It will accept and return non-tuple elements in the list (except nil), e.g. f(["some", "more"], []). To avoid this, you would need a slightly different logic.

  3. It will crash if the argument is not a proper list.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top