What is the equivalent of a for each within a for each?

Hi! Very new to elixir, thought I might try my hand at asking here, as I’m in a bit of a bind.

Suppose I have a list of maps as follows:

ball_prop_list = 
[
  %{"id" => "cue", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => -15.0, "z" => 0.0},
  %{"id" => "ball_1", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 15.0, "z" => 0.0},
  %{"id" => "ball_2", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 17.0, "z" => 1.1},
  %{"id" => "ball_3", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 17.0, "z" => -1.1}
]

How do I go about iterating through each item, and then comparing that item to the entire list (save for itself, of course)? This is based on C# code for checking ball collision, and I’m trying to roughly translate it to elixir.

foreach (bodyA in objectList) {
	foreach (bodyB in objectList) {
		if (bodyA == bodyB) {
			continue;
		}
		// Do other stuff here
	}
}

I tried this:

Enum.map(ball_prop_list , fn
	body_a ->
		Enum.map(ball_prop_list , fn body_b -> 
			if body_a["id"] != body_b["id"] do
				Sys.Log.debug("#{body_a["id"]} (A) vs #{body_a["id"]} (B)")
                # Do other stuff here
			end
		end)
end)

And enum.each in place of enum.map but it doesn’t seem to work as intended, as I end up with this:

cue (A) vs cue (B)
cue (A) vs cue (B)
cue (A) vs cue (B)
ball_1 (A) vs ball_1 (B)
ball_1 (A) vs ball_1 (B)
ball_1 (A) vs ball_1 (B)
ball_2 (A) vs ball_2 (B)
ball_2 (A) vs ball_2 (B)
ball_2 (A) vs ball_2 (B)
ball_3 (A) vs ball_3 (B)
ball_3 (A) vs ball_3 (B)
ball_3 (A) vs ball_3 (B)

So not only did it not check if it’s comparing the same item, it doesn’t seem to have checked against anything else; I was expecting something like:

cue (A) vs ball_1 (B)
cue (A) vs ball_2 (B)
cue (A) vs ball_3 (B)
ball_1 (A) vs cue (A)
ball_1 (A) vs ball_2 (B)
ball_1 (A) vs ball_3 (B)
etc

Is there anything else I can try?

for a <- objectList,
    b <- objectList -- [a],  # don't compare an object to itself
    a.x == b.x and a.z == b.z, do:  # I assumed this is the comparison logic for collision
    {:collision, a, b}   # This can be whatever form you want the response

Edit: to use a.x notation, the keys inside your maps must be atoms (:x instead of “x”). If you are using String keys, you would use a[“x”] notation to get the x-value instead.

1 Like

I have a function ready for that comparison, as it’s a bit more complex than comparing origin points, I apologize for not saying so in advance. How do I insert that somewhere in the code you provided?

Something like this?

for a <- objectList
    b <- objectList
    result = collisionComparisonFunction(a, b)

That didn’t work, I suppose the third line is expecting a condition or something.

for a <- objectList, # you need a comma after each comprehension clause
    b <- objectList -- [a],
    # don't forget to subtract a from the list, or the comprehension 
    # will pick a again as the value for b.  Also don't forget the comma
    collisionDetected?(a, b), do: 
    # "Test" clause function must return a Boolean.  
    # Functions that return Booleans should end in ?
    # After the last clause, put do:
    {:collision, a, b}
    # This is how the results will be returned (in a list).  For example:
    # [{:collision, obj5, obj99}, {:collision, obj1, obj7} ...]
    # where each obj is a map %{"id" => "ball_1", "is_idle" ...}
1 Like

To put it another way, comprehensions follow this syntax:

for a <- objectList,              # for clause 1, declares variable a
    b <- objectList -- [a],       # for clause 2, declares variable b
    collisionDetected?(a, b), do: # test clause, only truthy results pass
    {:collision, a, b}            # "Return statement": this is how we format passing results

You need all four lines for it to work. Let me know if you have any more questions.

1 Like

Look REAAAAAL close at the second #{} :slight_smile:

3 Likes
defmodule T do
  def check_balls do
    ball_prop_list = [
      %{
        "id" => "cue",
        "is_idle" => true,
        "velocity_x" => 0.0,
        "velocity_z" => 0.0,
        "x" => -15.0,
        "z" => 0.0
      },
      %{
        "id" => "ball_1",
        "is_idle" => true,
        "velocity_x" => 0.0,
        "velocity_z" => 0.0,
        "x" => 15.0,
        "z" => 0.0
      },
      %{
        "id" => "ball_2",
        "is_idle" => true,
        "velocity_x" => 0.0,
        "velocity_z" => 0.0,
        "x" => 17.0,
        "z" => 1.1
      },
      %{
        "id" => "ball_3",
        "is_idle" => true,
        "velocity_x" => 0.0,
        "velocity_z" => 0.0,
        "x" => 17.0,
        "z" => -1.1
      }
    ]

    compare(ball_prop_list)
  end

  def compare([head | rest_of_the_list]) do
    Enum.each(rest_of_the_list, fn another_ball ->
      IO.puts("#{head["id"]} vs #{another_ball["id"]}")

      # don't know what is a reason to compare in another order
      # but if it is necessary...
      IO.puts("#{another_ball["id"]} vs #{head["id"]}")
    end)

    compare(rest_of_the_list)
  end

  def compare([]), do: :done
end

iex(2)> T.check_balls()
cue vs ball_1
ball_1 vs cue
cue vs ball_2
ball_2 vs cue
cue vs ball_3
ball_3 vs cue
ball_1 vs ball_2
ball_2 vs ball_1
ball_1 vs ball_3
ball_3 vs ball_1
ball_2 vs ball_3
ball_3 vs ball_2
:done

Oh myyyy. That’s what I get for coding late at night.

Would this have worked? The logs (corrected) say it does, but I’m curious if there are any underlying performance issues (or why, in my searches, using comprehension, as @APB9785 suggested, is the “recommended” way).

I’m not sure if I should worry about those right now, since I’m just starting out, but this could be a good fall back plan if I can’t get the results of the formatting right.

You can use list comprehension as other has suggested, but the functional way is to use recursion:

defp collisionOfBall(%{"id" => id}, %{"id" => id}), do: false
defp collisionOfBall(ball1, ball2), do: whatever_check_i_have(ball1, ball2)

defp collisionOfList(_, []), do: false
defp collisionOfList([], _), do: false
defp collisionOfList([head1 | tail1], [head2 | tail2]) do
    collisionOfBall(head1, head2) ||
    collisionOfList([head1], tail2) ||
    collisionOfList(tail1, [head2]) ||
    collisionOfList(tail1, tail2) 
end

If you only have one list, that can be even simpler:

defp collision?([]), do: false
defp collision?([head | tail]) do
    Enum.any?(tail, fn each ->
        whatever_check_i_have(head, each)
    end) ||
    collision?(tail)
end
1 Like

Both nested Enum.map and the comprehension approach are O(N^2) when the list has N elements.

Note that the version that uses -- pays a LOT for that subtraction - it’s O(N) by itself! Alternatively, you could have your collision checker ignore duplicates.

I wouldn’t worry too much about performance - by the time N is large enough for it to be a problem, you’ll want to find a more efficient algorithm than either version (partitioning the list with a quadtree?).

One other note - the output of the two is different; the nested maps give a result that’s nested lists, while the comprehension gives a single flat result. Which one is better depends on what your application does with that data subsequently.

4 Likes

Thanks for this and for your patience. I suppose the next step would be to somehow update the original ball_prop_list (or create a new one) and update the values of that with the values from return, but that’s probably another question for another time.

Happy to help. For more Elixir practice with similar concepts, check out Advent of Code 2018 Day 10, and 2019 Day 12. Both ask you to simulate simple physics for many particles and eventually check for things such as convergence and cycling.