Reordering a list according to template, with minimal number of operations - list

I need to update a list according to a template list/array: add, remove and reorder elements. The list is prepopulated with elements mostly matching the template contents and ordering. Operations on the list are expensive. I have full control of the template structure, as I'm creating it, but I cannot change the interface of the list.
I want to update the list to match the template in as few operations as possible. Is there any algorithm that you would recommend? (I imagine that it would do some kind of scoring/distance calculation and then perform the operations.)
Available list operations:
insertBefore(element, otherId)
insertAfter(element, otherId)
append(element) (appends to end)
get(index)
get(id)
remove(index)
remove(id)
Each inserted element has a unique ID.
Examples:
Inital list:
Aragorn
Legolas
Gimli
Frodo
Harry
Ron
Target templates:
One swap
Aragorn
Gimli ^
Legolas v
Frodo
Harry
Ron
Moved cluster
Frodo ^
Harry ^
Ron ^
Aragorn
Legolas
Gimli
Add, remove and moves
(-)
Frodo ^
Gimli
Gollum +
Legolas v
Pippin +
(-)
(-)

Related

Prolog - split a list into lists of lists

The list to be split is a list of guests that attend a dinner. The dinner has three meals (appetizer, main, and dessert). The result of the type list of lists should be a list for each of the meals with the sub-lists showing the people who eat that meal together.
eg.:
Appetizer: [[Tick, Trick, Track],[Tic, Tac, Toe], [Jerry, Larry, Harry]]
Main: [[Tick, Tic, Jerry], [Trick, Tac, Larry],[Track, Toe, Harry]]
Dessert: [[Tick, Tac, Larry], [Trick, Toe, Harry], [Track, Tic, Jerry]]
The function I have to code:
make_dinner(?Starters, ?Main, ?Dessert, +List_of_Persons, +Group_size):-
Group size is the size of the groups (in the example above three)
I don't know how to approach this problem using prolog language. I can easily code this in Java, which I'm really good at. But the recursion and logic here are very confusing to me. There's no "return" argument, no "if" statement", just a bunch of commas :")
My idea was to:
find out how many groups I need (depends on group size and list length)
fill the appetizer list with the original order of the list. Count it off and split the list after every group size position to get the list of lists for an appetizer (the most intuitive way I think)
fill the first sub-list of the "main" list with the first element of the list, then the second element of the other sub-lists from the "appetizer" list. Fill the second sub-list with the second element of the first sub-list of "appetizer", then the third element of the next sub-lists in "appetizer", and so on and so forth.
If the number doesn't match (eg. list length 10 but group size 3), I will simply have one group that's smaller. No person eats twice with the same person.
Does my idea work in Prolog? Can I implement that logic?
I don't really know how to approach this task because I'm very new to Prolog. I will have the main function below, then I need a helper_function called get_number_of_groups to know how many elements go into each list. And I have one function to get the size of the list.
How do I make the starters, main, and dessert into a list of lists? I know about recursion but somehow I don't know where to go from here.
%make_rudi: main function
%return: starters, main, and dessert; each list of lists
make_rudi(Starters, Main, Dessert, List_of_Persons, Group_size):-
get_number_of_groups(List_of_Persons, Group_size, Number),
%get_number_of_groups(+List_of_Persons, +Group_size, ?Number)
%return: the number of groups I have for each meal
get_number_of_groups(List_of_Persons, Group_size, Number):-
Number is list_length(Xs, List_of_Persons)/Group_size.
%list_length(?Xs,+L)
%the length of the list
list_length(Xs,L) :-
list_length(Xs,0,L) .
list_length( \[\] , L , L ) .
list_length( \[\_|Xs\] , T , L ) :-
T1 is T+1 ,
list_length(Xs,T1,L).

Merge 2 lists in robot framework

Below are my 2 lists:
a: ['a0', 'a3']
b: ['b0', 'b1', 'b0', 'b1']
I need to merge the above 2 list as below:
final: ['a0b0', 'a0b1', 'a3b0', 'a3b1']
Need to write a for loop which will check for "0" in list b and then start with the next element in list a.
Thanks,
If I understand the rules, you need to combine two lists such as:
items from both lists will be combined, items from the first list will come first (e.g. "a0" and "b1" will become "a0b1")
there could be repeating items in both lists but you don't want these repetitions to be present in the final list
I think Python is your best friend here, I'd propose this piece of code as a custom library:
Libraries/ListUtils.py
def combine_lists(list1, list2):
set1 = set(list1)
set2 = set(list2)
final_list = []
for i1 in set1:
for i2 in set2:
final_list.append("{}{}".format(i1, i2))
return final_list
and an example test case:
*** Settings ***
Library ../Libraries/ListUtils.py
Library Collections
*** Test Cases ***
Combine Lists
${list1}= Create List a0 a3
${list2}= Create List b0 b1 b0 b1
${final_list}= Combine Lists ${list1} ${list2}
Log To Console ${final_list}
This will log into console:
['a3b1', 'a3b0', 'a0b1', 'a0b0']
which is the output you specified in your question. It's not ordered in any way, you might want to add this to it.
Also note that there's Combine Lists keyword in Collections library (but this one does something else than you want). As a result of that, my example will produce one warning:
[ WARN ] Keyword 'Combine Lists' found both from a custom test library 'ListUtils' and a standard library 'Collections'. The custom keyword is used. To select explicitly, and to get rid of this warning, use either 'ListUtils.Combine Lists' or 'Collections.Combine Lists'.
So you might want to pay more attention to naming conventions, or call keywords by their full name like e.g. Collections.Combine Lists.

Prolog: Sort a list by alternative index

I'm attempting to sort a list of colors, by a given preffered order. For example a list [r,z,z,w,g,g,r,z] sorted in this order [z,b,g,r,w], will give an end result of [z,z,z,g,g,r,r,w].
I tried using a basic bubblesort algorithme and adding a check to see which of first two terms would be 'higher' on the order list.
% take the to-sorted list, the order in which to sort the list, and the
% result.
%colourSort([r,z,z,w,g,g,r,z],[z,b,g,r,w],X). returns X = [z,z,z,g,g,r,r,w]
colourSort(List,Order,Sorted):-
swap(List,List1,Order),
!,
colourSort(List1,Order,Sorted).
colourSort(Sorted,_,Sorted).
% check if the either the first or second letter is first in the order
% list, if neither check the next letter in the order list.
check(A,_,[H|_],A):-
A == H.
check(_,B,[H|_],B):-
B == H.
check(A,B,[_|T],R):-
check(A,B,T,R).
check(_,_,[],_).
%swap incase a set of letters isn't ordered, continues otherwise.
swap([X,Y|Rest],[Y,X|Rest],Order):-
check(X,Y,Order,R),
X == R.
swap([Z|Rest],[Z|Rest1],Order) :-
swap(Rest,Rest1,Order).
When I run the code, it ends up crashing my swi-prolog, I'm assuming it's getting stuck in a loop or something, but haven't been able to figure out why or how. Any advice or tips would be appreciated.
Here's a solution to the stated problem, which does not, however, use a custom sorting algorithm. Instead, it uses the common pairs data-structure (using the (-)/2 operator to form a list of items Key-Value) and the keysort/2 for sorting. Edit: this answer has been reworked in accordance with #mat's tip in the comments, and to provide a more succinct explanation).
Solution:
item_with_rank(Ranking, Item, Rank-Item) :-
nth0(Rank, Ranking, Item).
sort_by_ranking(Ranking, ToSort, Sorted) :-
maplist(item_with_rank(Ranking), ToSort, Ranked),
keysort(Ranked, RankedSorted),
pairs_values(RankedSorted, Sorted).
Explanation:
We define a predicate item_with_rank(Ranking, Item, Rank-Item) that uses a list of arbitrarily ordered terms as a Ranking, and associates with the given Item a Rank which is equivalent to the 0-based index of the first term in Ranking that unifies with Item. We then define sort_by_ranking(Ranking, ToSort, Sorted). sort_by_ranking/3 uses maplist/3 to call item_with_rank/3, with the given Ranking, on each element of the list ToSort, obtaining a list of pairs, Ranked, assigning a rank to each item. We use keysort/2 to sort the Ranked so that they order of elements accords with the value of their "ranks" (keys) in RankedSorted. When we extract just the values from RankedSorted, we are left with the Sorted items, which is what we were after:
Example of usage:
?- sort_by_ranking([z,b,g,r,w], [r,z,z,w,g,g,r,z], S).
S = [z, z, z, g, g, r, r, w] ;
false.

In Erlang, how can I return an entire record from a list of records given an id value?

I see related issues and solutions in Google and in previous answers here, but they all baffle me.
Say I have a list of records, each with an id. Say:
-record(blah, {id, data}).
Record2#blah.id = 7
L = [Record1, Record2, ... ]
I'm looking for a function like get_record(List, ID) that will return the corresponding record in it's entirety, e.g.:
22> get_record(L, 7).
{blah, id=7, data="ta da!"}
Many thanks,
LRP
I
Internally, a record is a tuple of {Name, v1, v2}, so your example record will look like {blah, 7, data} as a tuple.
With this in mind, you can use the lists:keyfind/3 function to look up a record in a list:
lists:keyfind(7, #blah.id, L).
The first argument here is ID value, the second argument is the tuple index for the ID field and the third argument is the list.
The #Name.Field syntax allows you to get the field index for any record field.
You also could use a list comprehension, like
[R || R <- Records, R#blah.id == 7]
which will give you all records in the list that match the id.

Erlang: Sorting or Ordering Function for List of Tuple Lists

I have trouble sorting two related but separate lists of tuple lists. One list is made up of tuple lists representing a blog post. The other list is made up of tuple lists representing a comment post.
The problem is when you would like the same order based on blog id value. The lists for blog posts is sorted via the date value. So you cannot just sort numerically via blog id for both blog and comment post. And you cannot just sort the comment post via date value because the date values of blog and related comment post may be different.
I am not sure how to approach the problem - at least not in an elegant way.
Should I use lists:nth and consequently get each tuple list and position value? Then I would get the value of blog id, Then I would search in the list for comment posts for that id. Get the value of that tuple list. Associate the value of that tuple list in a new list with the appropriate nth position value.
Should I use the lists:sort function?
Any suggestions with code samples much appreciated.
Here are two sample lists of tuple lists that can be used as a basis :
[[{<<"blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2010-12-4T6:10:12">>},
{<<"message">>,<<"la di da bo di do">>}],
[{<<"blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2009-12-3T10:09:33">>},
{<<"message">>,<<"that is cool">>}],
[{<<"blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-2T18:12:29">>},
{<<"message">>,<<"i like san francisco">>}]]
[[{<<"comment_id">>,<<"n6">>},
{<<"related_blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2010-12-5T15:10:12">>},
{<<"message">>,<<"yup really neat">>}],
[{<<"comment_id">>,<<"y2">>},
{<<"related_blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-6T10:09:33">>},
{<<"message">>,<<"yes but rent is expensive">>}],
[{<<"comment_id">>,<<"x4">>},
{<<"related_blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2009-12-5T16:12:29">>},
{<<"message">>,<<"sounds like a hit">>}]]
And the desired output is the following with first list unchanged and second list reordered :
[[{<<"blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2010-12-4T6:10:12">>},
{<<"message">>,<<"la di da bo di do">>}],
[{<<"blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2009-12-3T10:09:33">>},
{<<"message">>,<<"that is cool">>}],
[{<<"blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-2T18:12:29">>},
{<<"message">>,<<"i like san francisco">>}]]
[ [{<<"comment_id">>,<<"x4">>},
{<<"related_blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2009-12-5T16:12:29">>},
{<<"message">>,<<"sounds like a hit">>}],
[{<<"comment_id">>,<<"n6">>},
{<<"related_blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2010-12-5T15:10:12">>},
{<<"message">>,<<"yup really neat">>}],
[{<<"comment_id">>,<<"y2">>},
{<<"related_blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-6T10:09:33">>},
{<<"message">>,<<"yes but rent is expensive">>}]]
Ok, new try then :)
We have:
-module(foo).
-compile(export_all).
Basic module exports to test the thing
blogs() ->
[[{<<"blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2010-12-4T6:10:12">>},
{<<"message">>,<<"la di da bo di do">>}],
[{<<"blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2009-12-3T10:09:33">>},
{<<"message">>,<<"that is cool">>}],
[{<<"blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-2T18:12:29">>},
{<<"message">>,<<"i like san francisco">>}]].
Your definition of blogs.
comments() ->
[[{<<"comment_id">>,<<"n6">>},
{<<"related_blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2010-12-5T15:10:12">>},
{<<"message">>,<<"yup really neat">>}],
[{<<"comment_id">>,<<"y2">>},
{<<"related_blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-6T10:09:33">>},
{<<"message">>,<<"yes but rent is expensive">>}],
[{<<"comment_id">>,<<"x4">>},
{<<"related_blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2009-12-5T16:12:29">>},
{<<"message">>,<<"sounds like a hit">>}]].
Your definition of comments.
sorted_comments() ->
[[{<<"comment_id">>,<<"x4">>},
{<<"related_blog_id">>,<<"a2">>},
{<<"postDate">>,<<"2009-12-5T16:12:29">>},
{<<"message">>,<<"sounds like a hit">>}],
[{<<"comment_id">>,<<"n6">>},
{<<"related_blog_id">>,<<"b8">>},
{<<"postDate">>,<<"2010-12-5T15:10:12">>},
{<<"message">>,<<"yup really neat">>}],
[{<<"comment_id">>,<<"y2">>},
{<<"related_blog_id">>,<<"a9">>},
{<<"postDate">>,<<"2009-12-6T10:09:33">>},
{<<"message">>,<<"yes but rent is expensive">>}]].
Your definition of being sorted.
sort(Blogs, Comments) ->
%% Create list of blog id's
Bs = [proplists:get_value(<<"blog_id">>, B) || B <- Blogs],
Fetch all the blog_id values from the Blogs.
%% Create the numbering
DB = dict:from_list([Item || Item <- lists:zip(Bs,
lists:seq(1, length(Bs)))]),
Number the order the blogs occur in. Stuff these into a dict for fast lookup later.
%% Sorter function:
F = fun(I, J) ->
II = proplists:get_value(<<"related_blog_id">>,
I),
JJ = proplists:get_value(<<"related_blog_id">>,
J),
dict:fetch(II, DB) =< dict:fetch(JJ, DB)
end,
This function compares two Comments, I, J to each other based on their related blog_id.
{Blogs, lists:sort(F, Comments)}.
Return what we want to return.
sort_test() ->
{blogs(), sorted_comments()} == sort(blogs(), comments()).
Tester function.
2> c(foo).
{ok,foo}
3> foo:sort_test().
true