(Protocol.UndefinedError) protocol Enumerable not implemented for %User{} struct

Hi.
I’m trying to map over a list of struct but it’s giving me this error

(Protocol.UndefinedError) protocol Enumerable not implemented for %User{} struct

[%User{
id: 1,
name: ron,
role: superuser
}]

So what I’m trying to do is first I’m filtering where role is “superuser”. So this is another function where I’m calling it
Enum.filter(users, fn user -> filter_users(user) end)

 def filter_users(users) do 
    if user.role == "superuser"
       Enum.map(users, fn a -> a.id end) # Here I'm receiving a list of users.
   end
 end

So here I’m receiving a list of users. and then I’m trying to fetch the id by mapping over the values.

But when I’m running this in iex shell and calling this filter_users function. I’m receiving a list of users.
Now when I’m mapping over the users by this function

  Enum.map(users, fn a -> a.id end)

It’s giving me a list of id for the users. But when I’m directly Enum.map(users, fn a -> a.id end) put this in a function and when I’m calling the filter function it’s giving me that error.

Why these two different behaviour is happening I’m confused.

user or users?

Well, the error tells You the reason :slight_smile:

I’m receiving a list of users. It’s based on the role and many resources has the same role. So its giving me all the resource which has “superuser” role

No, You are receiving a User… and You cannot Enum.map a User

What should I do then? I think my approach is wrong. I have a list of struct and I want to filter on the particular field which can return list
So that’s why first I called filter function then second function to handle the condition. Is my approach wrong?

The filter function should return a boolean.
You should not nest Enum.filter and Enum.map, but use them sequentially.

users
|> Enum.filter(# anonymous function returning a boolean... true if superuser, false if not)
|> Enum.map(& &1.id)

So I just read that we cannot filter and transform at the same time. Can we filter it based on condition?

What You wrote is…

users
|> Enum.filter(fn user -> 
  Enum.map(user, ...)  # <- wrong!
end)

You can do it with only one loop. But I let You try it by yourself… There are multiple solutions. My preferred choice is a list comprehension.

The filter function is a condition.

Let me try with it what I know :sweat_smile:. Never used comprehension though!

Just learned the comprehension

for u <- users do
      if u.role == "superuser" do
        u
      end
end

But is there a way to avoid nil in a list? It’s giving me something like this [nil, nil, nil, %User{}]

Not sure if you’re using a database. If so why not query the role superuser with a where clause using Ecto. Then you would only receive users with role of superuser.

For this data I’m not using ecto.

1 Like

You can pass a filter to the list comprehension…

for u <- users, u.role == "superkoko", do: u.id
3 Likes

You are rushing too much here and are not stopping to make what is a pretty beginner-friendly research: namely just study several of the functions in the Enum module. Your problem is super easy and fits in 2-3 lines of code with using Enum.

2 Likes