Reputation: 588
I need to make an address book in Erlang. I have done almost everything except for one function that's giving me problems.
My record is:
-record(contact, {fname, lname, phone=[], mail=[], city=[], street=[]}).
I have to write a function that will search through instances of contact
and find all with a particular city
name, and for those instances return {fname,lname}
tuples. Different contacts can of course have the same cities.
When I needed similar functions for the mail
and phone
fields I did it this way:
findByPhone(_,[]) -> {error,"not found"};
findByPhone(Phone,[H|T]) ->
case findPhoneForUser(Phone, H#contact.phone) of
true -> {H#contact.fname, H#contact.lname};
false -> findByPhone(Phone, T)
end.
findPhoneForUser(_,[]) -> false;
findPhoneForUser(Phone, [Phone|_]) -> true;
findPhoneForUser(Phone, [_|T]) -> findPhoneForUser(Phone, T).
But both mail
and phone
are unique values, so whenever one is found the function is finished. For city
, a search can yield multiple return values, so it must collect all matches.
How to handle this problem? I thought about list comprehensions something like:
{X,Y} || X<-H#contact.fname, Y<-H#contact.lname, City=:=H#contact.city
but it will return tuples from single ASCII codes :/
Upvotes: 2
Views: 137
Reputation: 20014
You can use a list comprehension. Assuming your address book is stored in a variable named AddressBook
, and the city you're matching is stored in a variable named City
, the following will work:
[{C#contact.fname, C#contact.lname} || C <- AddressBook, C#contact.city == City].
Also note that you can simplify your findByPhone
function using the lists:keyfind/3
function:
findByPhone(Phone, AddressBook) ->
case lists:keyfind(Phone, #contact.phone, AddressBook) of
#contact{fname=Fname, lname=Lname} -> {Fname, Lname};
false -> {error, not_found}
end.
This works because records are tuples under the covers. The construct #contact.phone
used as the second argument to lists:keyfind/3
provides the element number of the phone
field of the underlying tuple. In fact, you could write a single non-exported find
function supporting any unique field in your record with this approach, and then write exported functions for each such searchable field:
find(Value, Field, AddressBook) ->
case lists:keyfind(Value, Field, AddressBook) of
#contact{fname=Fname, lname=Lname} -> {Fname, Lname};
false -> {error, not_found}
end.
findByPhone(Phone, AddressBook) -> find(Phone, #contact.phone, AddressBook).
findByMail(Mail, AddressBook) -> find(Mail, #contact.mail, AddressBook).
Upvotes: 3