I'am struggling with records in one of my modules.
I defined on top of my code a record as:
-record(user, {pid,
name,
nick}).
in few words each user is going to be represented as process with its own pid and other fields.
Later on in the module I am doing the following:
Pid = UserPid,
GetUser = fun(X) ->
if X#user.pid =:= Pid -> true;
X#user.pid=/= Pid -> false
end
end,
User = lists:filter(GetUser, Users),
io:format("User pid is ~p~n",[User#user.pid]).
Running this code I get:
** exception error: {badrecord,user}
But if I do:
io:format("User ~p~n",[User]).
It prints
User [{user,<0.33.0>,name1,nick1}]
Can anyone point out what i am missing?
Thanks
The problems is that lists:filter
returns another list, not a single element. So you are basically trying to treat a list as a record. If you look carefully in the output of
io:format("User ~p~n",[User])
%% User [{user,<0.33.0>,name1,nick1}]
you will notice that the statement is wrapped in []
. It's a list. If you need just the first user use
[First | Rest] = lists:filter(GetUser, Users)
If you only want the pids use lists:map
:
UsersWithPid = lists:filter(GetUser, Users),
Pids = lists:map(fun(U) -> U#user.pid end, UsersWithPid).
Now Pids
is list with the pids of the users with pid.
Emil's answer about the lists:filter
function is correct.
This is how I would rewrite your code, though:
-module(com).
-record(user, {pid,
name,
nick}).
-export([lookup/1]).
lookup(Pid) ->
Users = users(),
FilteredUsers = [User || #user{pid = P} = User <- Users, Pid =:= P],
lists:foreach(fun display/1, FilteredUsers).
display(User) ->
io:format("User name is ~p~n",[User#user.name]).
users() ->
User1 = #user{pid = 1, name = "Bob", nick = "bob"},
User2 = #user{pid = 2, name = "Alice", nick = "alice"},
User3 = #user{pid = 1, name = "Charlie", nick = "charlie"},
[User1, User2, User3].
I'm assuming you can have multiple pids. If you don't, you can save yourself the foreach.
I believe that using list comprehensions in this case makes the code much more readable. Also, the following:
Pid = UserPid,
doesn't look very useful to me...
As others have pointed out, lists:filter/2
returns a list, even if it's just a single element. The function you're looking for is lists:keyfind/3
(in Erlang R14B03, for R13B04 and earlier, use lists:keysearch/3
):
Eshell V5.8.4 (abort with ^G)
1> rd(user, {pid, name, nick}).
user
2> Users = [#user{pid = spawn(fun() -> ok end), name = name1, nick = nick1},
2> #user{pid = spawn(fun() -> ok end), name = name2, nick = nick2},
2> #user{pid = spawn(fun() -> ok end), name = name3, nick = nick3}].
[#user{pid = <0.34.0>,name = name1,nick = nick1},
#user{pid = <0.35.0>,name = name2,nick = nick2},
#user{pid = <0.36.0>,name = name3,nick = nick3}]
3> lists:keysearch(pid(0,35,0), #user.pid, Users).
{value,#user{pid = <0.35.0>,name = name2,nick = nick2}}
4> lists:keyfind(pid(0,35,0), #user.pid, Users).
#user{pid = <0.35.0>,name = name2,nick = nick2}
5> lists:keyfind(pid(0,99,0), #user.pid, Users).
false
lists:keyfind/3
is preferred because it's simpler.
Using only #user.pid
returns the position of the field pid
in the #user
record:
6> #user.pid.
2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With