# Count number of occurrence in a list with condition

I’m using: SWI-Prolog version 8.4.3 for x64-win64

i have a list of predicates :

``````ListP = [
patient(201, 4, 2, inTime),
patient(202, 3, 2, late),
patient(203, 2, 3, late),
patient(204, 1, 3, late),
patient(204, 1, 4, inTime),
patient(203, 2, 4, late),
patient(204, 1, 5, inTime),
patient(204, 1, 5, inTime)
].
``````

and i want to count number of patients inTime categorized by their 3rd argument:

I want the result to be something like :

``````Occ = [
[patient(_, _, 2, inTime), 1],
[patient(_, _, 3, inTime), 0],
[patient(_, _, 4, inTime), 1],
[patient(_, _, 5, inTime), 2]
].
``````

What i tried :

``````count_occurrences(List, Occ):-
findall(
[patient(_,_,X,inTime),L],
(bagof(true,member(patient(_,_,X,inTime),List),Xs), length(Xs,L)),
Occ
).
``````

The result of my code :

``````Occ = [
[patient(_, _, 2, inTime), 1],
[patient(_, _, 4, inTime), 1],
[patient(_, _, 5, inTime), 2]
].
``````

it works well except that it does not give me the result for the patient whose 3rd argument is 3
`[patient(_, _, 3, inTime), 0]` is omitted.

Use Sorting predicates in PROLOG - #19 by brebs and adapt it to count.

It happens because bagof/3 fails if it cannot find any answer (in contrast, findall/3 succeeds with an empty list).
It’s not easy to fix your code, because you’re using bagof to produce the distinct values. Maybe

``````count_occurrences(ListP,CountInTime) :-
map_list_to_pairs(arg(3),ListP,Keyed),
group_pairs_by_key(Keyed,Grouped),
findall([patient(_,_,K,inTime),N],
(   member(K-L,Grouped),
aggregate_all(count,member(patient(_,_,K,inTime),L),N)
),CountInTime).
``````
1 Like

OMG, Yous saved my life ! Thanks so much.
̶C̶a̶n̶ ̶i̶ ̶u̶s̶e̶ ̶t̶h̶e̶ ̶s̶a̶m̶e̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶t̶o̶ ̶c̶o̶u̶n̶t̶ ̶n̶u̶m̶b̶e̶r̶ ̶o̶f̶ ̶p̶r̶e̶d̶i̶c̶a̶t̶e̶s̶,̶ ̶c̶l̶a̶s̶s̶i̶f̶i̶e̶d̶ ̶b̶y̶ ̶t̶h̶e̶i̶r̶ ̶3̶r̶d̶ ̶a̶r̶g̶u̶m̶e̶n̶t̶.̶

̶t̶o̶ ̶g̶e̶t̶ ̶s̶o̶m̶e̶t̶h̶i̶n̶g̶ ̶L̶i̶k̶e̶ ̶:̶

``````Occ = [
[patient(_, _, 2, _), 2],
[patient(_, _, 3, _), 2],
[patient(_, _, 4, _), 2],
[patient(_, _, 5, _), 2]
].
``````

SOLVED :

I just changed the inTime in the code to a _

``````count_occurrences_all(ListP,CountInTime) :-
map_list_to_pairs(arg(3),ListP,Keyed),
group_pairs_by_key(Keyed,Grouped),
findall([patient(_,_,K,_),N],
(   member(K-L,Grouped),
aggregate_all(count,member(patient(_,_,K,_),L),N)
),CountInTime).
``````

If don’t need to discriminate by inTime, then could be a bit easier:

``````count_occurrences_all(ListP,Counted) :-
map_list_to_pairs(arg(3),ListP,Keyed),
group_pairs_by_key(Keyed,Grouped),
findall([patient(_,_,K,_),N],
(   member(K-L,Grouped),
length(L,N)
),Counted).
``````

or

``````count_occurrences_all(ListP,Counted) :-
map_list_to_pairs(arg(3),ListP,Keyed),
group_pairs_by_key(Keyed,Grouped),
maplist([K-L,[patient(_,_,K,_),N]]>>length(L,N),Grouped,Counted).
``````

Always try to keep the logic as simple as possible…

1 Like

How can i iterate throught both lists so i can make operation between the second element of each sublist :

``````Occ = [
[patient(_, _, 2, inTime), 1],
[patient(_, _, 3, inTime), 0],
[patient(_, _, 4, inTime), 1],
[patient(_, _, 5, inTime), 2]
].

Occ_all = [
[patient(_, _, 2, _), 2],
[patient(_, _, 3, _), 2],
[patient(_, _, 4, _), 2],
[patient(_, _, 5, _), 2]
].
``````

For exemple division:

``````1/2
0/2
1/2
2/2
``````

To get the following list :
`ResList=[ 0.5, 0, 0.5, 1].`

maplist/4 … something like this:

``````div([_,X], [_,Y], Z) :- Z is X/2.
?- maplist(div, Occ, Occ_all, Result).
``````