Iterating or Pattern Matching on a certain conditions

I have a pretty straight-forward problem, but struggle to make it clear and concise (is that possible at all!?).

So having two lists:

list_1 = ["$100", "$200", "$300"]
list_2 = ["bob", "$200", "sam"]

success output should be:

"bob: $100, sam: $300"

fail output:

list_1 = ["$100", "$200", "$300"]
list_2 = ["bob", "bob", "sam"]
false

Obviously, list_2 (or our pattern) might be different, but if prices are matching we should return a success output similar to above.

I approached it right away by:

iex(2)> Enum.zip(["bob", "$200", "sam"], ["$100", "$200", "$300"])
[{"bob", "$100"}, {"$200", "$200"}, {"sam", "$300"}]

and then with a lot of conditional logic and accumulators in Enum.reduce solved the problem, but it’s super ugly… When my Elixir code is ugly - I’m always questioning myself, since there is always another and cleaner way.

How would you approach this? Is there a way?

Thank you all in advance!

Based solely on what you’ve posted (I don’t really understand why list_2 has a mix of names and amounts):

list_1 = ["$100", "$200", "$300"]
list_2 = ["bob", "$200", "sam"]

pairs = Enum.zip(list_1, list_2)

unmatched_pairs = Enum.reject(pairs, fn {l1, l2} -> l1 == l2 end)

if length(unmatched_pairs) == length(list_1) do
  false
else
  unmatched_pairs
  |> Enum.map(fn {l1, l2} -> "#{l1}: #{l2}" end)
  |> Enum.join(", ")
end

(Edited to fix a bug - was looking at length(pairs) without filtering first)

1 Like

@al2o3cr
Matt, thank you so much! Always always appreciate your wisdom and willingness to help the community!

I’m sorry if my explanation wasn’t very clear (I know you a huge proponent of clear statements to make it easier for people to help efficiently).

I don’t really understand why list_2 has a mix of names and amounts

Well, that’s the thing - it’s a filter pattern (if I can call it this way). Let me give a couple of more real examples to explain it better:

1. 
list_1 = ["$100", "$200", "$300"]
list_2 = ["bob",  "$200", "sam"]

is a success case, since second elements in both lists are identical 
(and we don't need to expose it in our output).

And output will be:
"bob: $100, sam: $300"
(
- "bob" is a first element in the list_2 and 
- corresponding first element in the list_2 is "$100" and etc 
)
2. 
list_1 = ["$100", "$200", "$300"]
list_2 = ["$100",  "tom", "sam"]

is a success case for the same reasons in the ex 1.

And output will be:
"tom: $200, sam: $300"
(
- "tom" is a second element in the list_2 and 
- corresponding second element in the list_2 is "$200" and etc 
)
3. 
list_1 = ["$100", "$200", "$300"]
list_2 = ["bob",  "tom", "sam"]

is a fail case .

We don't have any corresponding matches on price (there are only names in the list_2)

I run your example, but not sure why we need if statement to check if pairs size equal to list_1?!
It will always be the same, no? Am I missing something here?

Again, really appreciate all your possible help here!

Nope, you’re not missing anything - that was a bug! :slight_smile:

The intent is to check the length of the zipped pairs after filtering out ones like {"$200", "$200"} - if none of the pairs got filtered out, we need to return false (a “fail case”).

1 Like

i too dont think i understand the 2 lists, but here is what i have for your 3 tests/examples:

iex(5)> test1 = {["$100", "$200", "$300"], ["bob",  "$200", "sam"]}
{["$100", "$200", "$300"], ["bob", "$200", "sam"]}
iex(6)> test2 = {["$100", "$200", "$300"], ["$100",  "tom", "sam"]}
{["$100", "$200", "$300"], ["$100", "tom", "sam"]}
iex(7)> test3 = {["$100", "$200", "$300"],["bob",  "tom", "sam"]}
{["$100", "$200", "$300"], ["bob", "tom", "sam"]}
iex(8)> [test1, test2, test3] |>
...(8)> Enum.map(fn {list1, list2} ->
...(8)>   Enum.zip(list1, list2) |>
...(8)>   Enum.reduce({false, []}, fn
...(8)>     {l1,l1}, {_, pairs} -> {true, pairs}
...(8)>     l1l2, {result, pairs} -> {result, [l1l2|pairs]}
...(8)>   end)
...(8)> end)
[
  true: [{"$300", "sam"}, {"$100", "bob"}],
  true: [{"$300", "sam"}, {"$200", "tom"}],
  false: [{"$300", "sam"}, {"$200", "tom"}, {"$100", "bob"}]
]

result/accumulator of the reduce is a tuple: {PASS-FAIL, PAIRS}

1 Like

Format more nicely in production code, but here’s a quick iex solution

iex(91)> list_1 = ["$100", "$200", "$300"]
["$100", "$200", "$300"]
iex(92)> list_2 = ["$100",  "tom", "sam"]
["$100", "tom", "sam"]
iex(93)> pairs = Enum.zip(list_1, list_2)
[{"$100", "$100"}, {"$200", "tom"}, {"$300", "sam"}]
iex(94)> if Enum.any?(pairs, &match?({a, a}, &1)), do: Enum.join((for {a, b} <- pairs, a != b, do: "#{b}: #{a}"), ", "), else: false
"tom: $200, sam: $300"
2 Likes

@czrpb and @gregvaughn
Thank you so much for your input and sorry about my delay here! That pandemic definitely is not over, got sick finally :frowning:

Again, really appreciate it!

1 Like