3

I've been looking at what I can do with Enum, Stream and for comprehensions and I'm struggling to reproduce left outer join behavior. I can implement a left outer join function with Enum.reduce but if there is some way to do that with for I'd rather use that.

I know python supports it, I've seen it here and here, and I suppose Elixir's comprehensions were inspired by python's. Can we do it in Elixir?

Suppose I have two lists that came from an external API or some json/xml file:

categories = [%{id: 1, name: "beverages"}, %{id: 2, name: "vegetables"}]
products = [%{name: "ice tea", category: 1}, %{name: "sake", category: 1}]

I want to left outer join them to get something like:

cat_product == [
  %{category: "beverages", product: "ice tea"},
  %{category: "beverages", product: "sake"},
  %{category: "vegetables", product: "(No product)"}
]

And something like:

cat_products == [
  %{name: "beverages", products: [list of products]}
  %{name: "vegetables", products: []}
]
Community
  • 1
  • 1
Juliano
  • 2,402
  • 1
  • 20
  • 22

1 Answers1

3

Your first example cannot be elegantly written with a for loop on the outside, because there can be records in the left list that are joined to more than one record in the right list. A for comprehension will however produce at most one result per element in the original collection. In this case, it would be more suitable to use Enum.flat_map:

Enum.flat_map categories, fn(c) ->
  case Enum.filter(products, fn(p) -> p.category == c.id end) do
    [] ->
      [%{name: c.name, product: nil}]
    prods ->
      for p <- prods, do: %{name: c.name, product: p.name}
  end
end

Your second example can be easily implemented just with for comprehensions, since there is always exactly one element in the result set per element in the original collection. Instead of using Enum.filter, the filtering is done with the second parameter to the inner for comprehension which makes the code much cleaner.

for c <- categories do
  prod = for p <- products, p.category == c.id do
    p.name
  end
  %{name: c.name, products: prod}
end
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168