Savvas Th
Savvas Th

Reputation: 75

Create a list of Key-Value pair

I have these facts:

name(john),
name(mary),
name(jack),

When I "iterate" all facts I find for every name something like a rating(a number). I want that rating to be paired with the name something like 2-john or john-2, store them in a list and sort the list based on the number. How can I do that in ProLog?

name(PersonsName), findRating(PersonsName, Rating), fail.

Upvotes: 1

Views: 385

Answers (1)

Guy Coder
Guy Coder

Reputation: 24976

@false gave the correct answer in the comment:

setof(V-K, ( name(K), rating(K, V) ), VKs)

Personal side joke for @false, that was more than :-. :)

I will just expand on it here for those needing the missing parts.

First the facts need to be facts, i.e.

name(john), is not a fact because it ends with a ,. A fact needs to end with a period (.).

Next facts are needed to associate a rating with the person.

rating(john,2).
rating(mary,3).
rating(jack,4).

Now for setof/3

The signature of setof/3 is setof(+Template, +Goal, -Set)

Goal is the query that will return the needed data, in this case goal is name(K), rating(K,V), but since that is more than one statement it is wrapped in () becoming ( name(K), rating(K, V) )

Example run:

?- name(Name),rating(Name,Rating).
Name = john,
Rating = 2 ;
Name = mary,
Rating = 3 ;
Name = jack,
Rating = 4.

While that gives us the data needed it is not in a format that is useful and returns the results one by one.

Template is the format of the data in the result, in this case V-K, where V for Value is the rating and K for Key is the key. The result will be like 2-john.

Here is an example of what we have so far demonstrated with a query and example run:

rating(V-K) :-
    name(K),
    rating(K,V).

?- rating(Ratings).
Ratings = 2-john ;
Ratings = 3-mary ;
Ratings = 4-jack.

Notice that the results are cumming back in the correct format but still not in a list.

Set is what is collected into the result.

Since you asked for a sorted result, setof/3 is used instead of bagof/3. As noted in setof/3: sorts the result using sort/2 to get a sorted list of alternatives without duplicates.

To make it easier to run the query instead of typing the entire setof/3 each time, just put it into a predicate.

ratings(VKs) :-
    setof(V-K,(name(K),rating(K,V)),VKs).

Complete code.

name(john).
name(mary).
name(jack).

rating(john,2).
rating(mary,3).
rating(jack,4).

ratings(VKs) :-
    setof(V-K,(name(K),rating(K,V)),VKs).

Example run:

?- ratings(VKs).
VKs = [2-john, 3-mary, 4-jack].

Note: This could also be done using library(assoc): Association lists but for this problem using setof/3 is so much more concise.

Upvotes: 2

Related Questions