Say I have a list:
Os = [(4,P1),(9,P2),(4,P3),(1,P4),(9,P5)].
I want to put every second element of the tuple in a bag that has the same first element like this:
SortedOs = [(4,[P1,P3]),(9,[P2,P5]),(1,P4)].
Currently I'm using bagof/3:
findall(
(O,Bag),
bagof(P,member((O,P),Os),Bag),
SortedOs
).
But instead it gives me the sorted list like this:
SortedOs = [(1,P4),(4,[P1,P3]),(9,[P2,P5])]
Which means bagof/3 looks up the first element in ascending order. Is there any way I can change this to get the list I want? Many thanks.
If you are using SWI-Prolog and you can change the format of the data from tuples to Pairs, (Key-Value) then you can use group_pairs_by_key/2.
Note that for group_pairs_by_key/2 to work correctly the input list must be sorted.
Code
example(Pairs0,Result) :-
sort(Pairs0,Pairs1),
group_pairs_by_key(Pairs1,Result).
Example usage
?- Pairs = [4-P1,9-P2,4-P3,1-P4,9-P5],example(Pairs,Result).
Pairs = [4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].
SWI-Prolog has a library of predicates for working with ordered sets (sorted list). ordsets.pl -- Ordered set manipulation
Here is a variation that checks if the input is an ordered set and skips the sorting if it is an ordered set.
example(List,Result) :-
(
is_ordset(List)
->
Order_set = List
;
list_to_ord_set(List,Order_set)
),
group_pairs_by_key(Order_set,Result).
Example usage
?- List = [1-P4, 4-P1, 4-P3, 9-P2, 9-P5],example(List,Result).
List = [1-P4, 4-P1, 4-P3, 9-P2, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].
?- List = [4-P1,9-P2,4-P3,1-P4,9-P5],example(List,Result).
List = [4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].
Note: Ordered Sets remove duplicates so using list_to_ord_set/2 instead of sort/2 will remove duplicates.
?- List = [4-P1,4-P1,9-P2,4-P3,1-P4,9-P5],example(List,Result).
List = [4-P1, 4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].
group_pairs_by_key/2 (source code)
Related
Let's say we have an existing empty list, m. I would like to append some elements (let's say three 7's) to m so that m is now [7,7,7]. How to do this using list iterators?
I have the following solution, which is not complete:
m = []
m.append([7 for i in range(3)])
I get m = [[7,7,7]] but not [7,7,7].
I am not looking for post-processing like making m flat such as:
flat_list = [item for sublist in l for item in sublist], because it is an extra step.
You may want to try extend rather than append.
m = []
m.extend([7 for i in range(3)])
Here is a post that may be helpful
I've a problem with the nested lists. I want to compute the lenght of the intersection of two nested lists with the python language. My lists are composed as follows:
list1 = [[1,2], [2,3], [3,4]]
list2 = [[1,2], [6,7], [4,5]]
output_list = [[1,2]]
How can i compute the intersection of the two lists?
I think there are two reasonable approaches to solving this issue.
If you don't have very many items in your top level lists, you can simply check if each sub-list in one of them is present in the other:
intersection = [inner_list for inner in list1 if inner_list in list2]
The in operator will test for equality, so different list objects with the same contents be found as expected. This is not very efficient however, since a list membership test has to iterate over all of the sublists. In other words, its performance is O(len(list1)*len(list2)). If your lists are long however, it may take more time than you want it to.
A more asymptotically efficient alternative approach is to convert the inner lists to tuples and turn the top level lists into sets. You don't actually need to write any loops yourself for this, as map and the set type's & operator will take care of it all for you:
intersection_set = set(map(tuple, list1)) & set(map(tuple, list2))
If you need your result to be a list of lists, you can of course, convert the set of tuples back into a list of lists:
intersection_list = list(map(list, intersection_set))
What about using sets in python?
>>> set1={(1,2),(2,3),(3,4)}
>>> set2={(1,2),(6,7),(4,5)}
>>> set1 & set2
set([(1, 2)])
>>> len(set1 & set2)
1
import json
list1 = [[1,2], [2,3], [3,4]]
list2 = [[1,2], [6,7], [4,5]]
list1_str = map(json.dumps, list1)
list2_str = map(json.dumps, list2)
output_set_str = set(list1_str) & set(list2_str)
output_list = map(json.loads, output_set_str)
print output_list
How can I sort/display all different outputs that make a predicate true according to their list size?
for example, if the output before is:
X = [1,2,3,5,5,2,1,4,1]
X = [1,2,3,1]
X = [1,2,1,3,1,3]
What can I do so that it outputs like this:
X = [1,2,3,1]
X = [1,2,1,3,1,3]
X = [1,2,3,5,5,2,1,4,1]
If your procedure always returns a list, you can collect all solutions, sort them according to the list length and then iterate over the sorted list.
E.g., assume you have a procedure something(L) which returns a list, you can do this:
sorted_something(L):-
findall(Len-L, (something(L), length(L, Len)), AllL),
keysort(AllL, SortedAllL),
member(_-L, SortedAllL).
The call to findall/3 will collect all solutions and their lengths, keysort/2 will sort the solutions according to their length, and member/2 obtains each list from the sorted list of solutions.
sort and duplicates removal can be performed by setof/3:
:- meta_predicate(sorted_lists(1,?)).
sorted_lists(Generate_a_list, SortedByLenght):-
setof(Len-L, (call(Generate_a_list, L), length(L, Len)), All),
member(_-SortedByLenght, All).
Let's say I have the following list:
List = [[a],[a,b],[a,c],[b,c],[b,d],[a,b,c],[a,b,d],[b,c,e],[b,d,e,f]]
The goal is to remove every list in the list that is a superset of a list in the list.
The list that contains the lists always has the following properties:
The lists in the list are sorted by length
Each list in the list is sorted
My initial idea was to simply start with the first list in the list and go through all other lists and remove the lists that are a superset. Next I'd look at the second list, et cetera.
After removing all supersets of the list [a] it should look like this:
List = [[a],[b,c],[b,d],[b,c,e],[b,d,e,f]]
Next the supersets of [b,c] should be removed:
List = [[a],[b,c],[b,d],[b,d,e,f]]
Last is the supersets of [b,d]:
List = [[a],[b,c],[b,d]]
And the line above should be the result.
I already made a predicate akin to the member predicate, but instead of taking a single element and comparing it to the list, this takes an entire list and compares it to the list:
memberList([],_).
memberList([X|Xs],Y) :-
member(X,Y),
memberList(Xs,Y).
This only works with lists.
?- memberList(a,[a,b,c]).
false.
?- memberList([a],[a,b,c]).
true .
?- memberList([a,b],[a,b,c]).
true .
But after this I'm a bit lost.
I tried the following which should remove the supersets of a single set, but it did not work:
removeSupersetsList(_,[],[]).
removeSupersetsList(X,[Y|Ys],[Y|Out]) :-
not(memberList(X,Y)),
removeSupersetsList(X,Ys,Out).
removeSupersetsList(X,[Y|Ys],Out) :-
memberList(X,Y),
removeSupersetsList(X,Ys,Out).
So I was wondering if someone could point me in the right direction to remove all supersets from a list or maybe even give the right predicate.
I'm using SWI-Prolog, where I find a crafted libray for ordered sets, and the required test, then using select/3 it's really easy to sanitize the list
rem_super_sets([], []).
rem_super_sets([L|Ls], R) :-
( select(T, Ls, L1), % get any T in Ls
ord_subset(L, T) % is T a superset of L ?
-> rem_super_sets([L|L1], R) % discard T, keep L for further tests
; R = [L|L2],
rem_super_sets(Ls, L2)
).
here a verification and the result
test :-
List = [[a],[a,b],[a,c],[b,c],[b,d],[a,b,c],[a,b,d],[b,c,e],[b,d,e,f]],
rem_super_sets(List, R),
write(R).
?- test.
[[a],[b,c],[b,d]]
true.
memberList([],_).
memberList([X|Xs],Y) :- member(X,Y),
memberList(Xs,Y).
%remove(ToRemove,ListWithSublists,LocRez,FinalRez)
%A list from ListWithSublists is removed,depending on ToRemove
% LocRez is accumulator used to obtain the FinalRez ( at the end )
remove(_,[],LocRez,LocRez) :- !.
remove(ToRemove,ListWithSublists,LocRez,FinalRez) :- ListWithSublists=[Sublist|Rest],
memberList(ToRemove,Sublist),
remove(ToRemove,Rest,LocRez,FinalRez),!.
remove(ToRemove,ListWithSublists,LocRez,FinalRez) :- ListWithSublists=[Sublist|Rest],
not(memberList(ToRemove,Sublist)),
append(LocRez,[Sublist],LocRezNew),
remove(ToRemove,Rest,LocRezNew,FinalRez),!.
> removeSupersetsList(List,Rez) :- removeSupersetsList(List,[],Rez). % call this for testing
%removeSupersetsList(List,LocRez,Final)
%remove the Head from List from the List itself if needed(obtain Rez in the process)
%append the Head into our LocRez(get LocRezNew),
%call this recursively for the Rez
removeSupersetsList(List,LocRez,LocRez) :- List=[] ,!.
removeSupersetsList(List,LocRez,Final) :- ( List=[ToRemove|_] ; List=[ToRemove] ),
remove(ToRemove,List,[],Rez),
append(LocRez,[ToRemove],LocRezNew),
removeSupersetsList(Rez,LocRezNew,Final),!.
How can I quickly swap two elements in Erlang list?
For example I have list: [1,2,3,4], how can I quickly get [1,3,2,4]?
You did not say in your question how you want to specify which two element you want to swap.
If you just want to swap the elements at position 2 and 3 in a list (like in your example):
swap23([A,B,C|Rest]) ->
[A,C,B|Rest].
Well if you wish to swap any index with another you can do the following
swap(List,S1,S2) -> {List2,[F|List3]} = lists:split(S1-1,List),
LT = List2++[lists:nth(S2,List)|List3],
{List4,[_|List5]} = lists:split(S2-1,LT),
List4++[F|List5].
With this code you only traverse the list once.
swap(A, B, List) ->
{P1, P2} = {min(A,B), max(A,B)},
{L1, [Elem1 | T1]} = lists:split(P1-1, List),
{L2, [Elem2 | L3]} = lists:split(P2-P1-1, T1),
lists:append([L1, [Elem2], L2, [Elem1], L3]).
Usage example:
1> swap(6,2,[1,2,3,4,5,6,7,8]).
[1,6,3,4,5,2,7,8]