Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

erlang records trouble

Tags:

erlang

records

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

like image 480
user601836 Avatar asked Jul 21 '11 08:07

user601836


3 Answers

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.

like image 62
Emil Ivanov Avatar answered Oct 19 '22 01:10

Emil Ivanov


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...

like image 31
Roberto Aloi Avatar answered Oct 19 '22 00:10

Roberto Aloi


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
like image 4
Adam Lindberg Avatar answered Oct 18 '22 23:10

Adam Lindberg