As you can see in the list I am having two same entries {:a, "John", 1} and {:a, "John", -1}. The sum of last element gives us 0 and hence i want to delete it from the list.
How can i get the output where my final result looks like
The tuple (being of variable len) is not very convenient here, because you have to isolate the last element and use the rest to keep track.
So I’d first think about using sth like
{{:a, "Alpha", 28}, 1}
instead. (But you can go on with the flat tuple also, will become a little messy though.
Then you could reduce the list with %{} as accumulator init and for each list-element use
Map.update/4
to accumulate your ‘actions’.
Map.update(acc, key, action, fn old -> old + action end)
with eg {:a, "Alpha", 28} = key, 1 = action.
Then you just map over the resulting dict and throw all with 0 values away.
Is the elimination of these elements based on the last element of the tuple, always?
Also, what makes those tuples “same”? You say that those starting with {:a, "John", ...} are same but then you also have a tuple that’s only {:b, 1}. What would another “same” tuple be that starts with :b?
OK, I needed 30 minutes break from work so I tried my hand at this.
Assuming that the “key” of a tuple is all except the last element, and the value (append / delete) is always only the last element, then this code does what you need:
defmodule Play
defguard is_valid_record(x) when is_tuple(x) and tuple_size(x) > 1
def input,
do: [
{:a, "John", 1},
{:b, 1},
{:a, "Alpha", 28, 1},
{:a, "John", -1}
]
def key(tuple) when is_valid_record(tuple) do
Tuple.delete_at(tuple, tuple_size(tuple) - 1)
end
def value(tuple) when is_valid_record(tuple) do
elem(tuple, tuple_size(tuple) - 1)
end
def combine({a, b}) when is_tuple(a), do: Tuple.append(a, b)
def process(list) when is_list(list) do
list
|> Enum.map(fn tuple -> {key(tuple), value(tuple)} end)
|> Enum.reduce(%{}, fn {key, value}, acc ->
Map.update(acc, key, value, fn previous_value -> value + previous_value end)
end)
|> Enum.reject(fn {_key, value} -> value == 0 end)
|> Enum.map(&combine/1)
end
def test do
input() |> process()
end
end
While I do support the idea of using an appropriate data structure for solving a certain problem it’s not much more code converting the datastructure on demand:
l = [
{:a, "John", 1},
{:b, 1},
{:a, "Alpha", 28, 1},
{:a, "John", -1}
]
intermediate =
Enum.reduce(l, %{}, fn tuple, acc ->
{action, key} = tuple |> Tuple.to_list |> List.pop_at(-1)
Map.update(acc, List.to_tuple(key), action, fn old -> old + action end)
end)
result = for {k, v} <- intermediate, v != 0, do: Tuple.append(k, v)
assert [{:b, 1}, {:a, "Alpha", 28, 1}] == result