Enum.reduce not working when acc is a map

I seriously struggle with Enum.reduce. I just can’t get my head wrapped around it but I know it is a valuable tool and I’m determined to understand it better.

I’m trying to write a function that uses it to run Repo.preload on a map based on a list of specified preloads.

So I have a map that is an Event with a bunch of fields and many relationships. When I call get_event, I don’t want to always preload everything. I want to pass the event_id and a preload_list of the relationships to preload:

So a preload list would look like this:

[:group, :users, :parent, :reminders]

Here’s the first Enum.reduce function I wrote (which throws errors):

def get_event(id, preload_list) do
    event = Repo.get(Event, id)
    Enum.reduce(preload_list, event, fn  ->
      preload, event -> Repo.preload(event, preload)
    end)
  end

Preload is essentially “x” and event is “acc”. It says "syntax error before the comma between Preload and Event … but I’m giving it the parameters specified in Enum.reduce(enumerable, acc, fun).

OK … so then I tried this:

def get_event(id, preload_list) do
    event = Repo.get(Event, id)
    Enum.reduce(preload_list, event, fn  ->
      :group, event -> Repo.preload(event, :group)
      :users, event -> Repo.preload(event, :users)
      :reminders, event -> Repo.preload(event, :reminders)
      :parent, event -> Repo.preload(event, :parent)
    end)
  end

Threw the same error. What am I doing wrong? Is the problem that I can’t use a map as an “acc” or that I can’t pass atoms as “x”?

Feel free to tell me if I’m approaching this in the totally wrong way and using the wrong Enum. Certainly won’t be the first time I went down the wrong path! LOL. I just thought Enum.reduce might be the right path since it accumulates the results.

You have a syntax error:

It should be fn params → code end or in your case I think you are just missing a case statement.

def get_event(id, preload_list) do
event = Repo.get(Event, id)
Enum.reduce(preload_list, event, fn preload, event →
case preload do
:group → Repo.preload(event, :group)
:users → Repo.preload(event, :users)
:reminders → Repo.preload(event, :reminders)
:parent → Repo.preload(event, :parent)
end
end)
end

So the example in the hex docs has:

Enum.reduce([1, 2, 3], 0, fn x, acc -> x + acc end)

My “x” is preload which is one of the preloads in my preload_list. My “acc” is event. So both of those are the params … right?

Ohhhhh … I have to put a case statement in there. I thought anonymous functions allowed for pattern matching on the params. Is that not the case?

I edited my response. Just add case

1 Like

If you are doing pattern matching you would leave off the first arrow

1 Like

Ohhhhhhh … right. I see the mistake. I put the first arrow after fn which threw it off. That did the trick. Thank you so much!

Yeah you had an anonymous function with an arity of 0 going.

1 Like

Hey @annad I think you’ve gotten some good help with your reduce issue, so I’ll just point out that Repo.preload works with a list. That is to say you can just do:

Repo.preload(event, preload_list)

You don’t actually need to do the reduce! Good learning experience though :smiley:

7 Likes

Hand smacking forehead. ha ha ha ha

Well … I guess I learned two things from this experience. Thank you for letting me know about that preload option. I will definitely take advantage of that!

1 Like

Not that you need it for preload, only to help you the next time you want to make a multi-clause anonymous function. You almost had it, but for an extra ->:

Enum.reduce(preload_list, event, fn # -> <— this was causing your problem 
  :group, event -> Repo.preload(event, :group)
  :users, event -> Repo.preload(event, :users)
  :reminders, event -> Repo.preload(event, :reminders)
  :parent, event -> Repo.preload(event, :parent)
end)

When creating an anonymous function, the -> comes after the arguments for each clause, and each clause needs the same number of arguments.

1 Like