Joining two lists with common elements - Scala - list

I have 2 Lists as mentioned below:
val L1 = List[(Int,Int,String)]
val L2 = List[(Int,Int,String)]
I want to join these 2 lists on the basis of 1st and 2nd Int element in a way that result list must have 4 elements (Int,Int,String,String).
val joinedList = List[(Int, Int, String, String)]
where last 2 String elements will be string from L1 and string from L2.
Ex:-
val L1 = List((1,1,"one"), (2,2,"two"))
val L2 = List((2,2,"twice"), (3,3,"thrice"))
Output List ->
val joinedList = List((1,1,"one","--"),(2,2,"two","twice"), (3,3,"--","thrice"))
Kindly suggest a way to achieve that in scala.

First you want to get it into Maps, so it's easier to look up, then you can just map over all the keys:
val L1Map = L1.map{case (x,y,z) => ((x,y) -> z)}.toMap
val L2Map = L2.map{case (x,y,z) => ((x,y) -> z)}.toMap
val allKeys = L1Map.keySet ++ L2Map.keySet
val result = allKeys map {case (x,y) =>
(x, y, L1Map.getOrElse((x,y), "--"), L2Map.getOrElse((x,y), "--"))
}
That gives you an unsorted Set as a result. If you need a List, you can convert it back and sort it as necessary.

Related

creating a list of lists based on another list content

I have 2 lists. They will always be the same length with respect to each other and might look like this toy example. The actual content is not predictable.
val original = [1, 2, 0, 1, 1, 2]
val elements = ["a","b","c","d","e","f"]
I want to create the following list:
val mappedList = [["c"],["a","d","e"],["b","f"]]
0 1 2
So the pattern is to group elements in the elements list, based on the value of the same-position element in original list. Any idea how can I achieve this in SML? I am not looking for a hard coded solution for this exact data, but a general one.
One way is to first write a function which takes an ordered pair such as (2,"c") and a list of ordered pairs such as
[(3,["a"]),(2,["b"]),(1,["a","e"])]
and returns a modified list with the element tacked onto the appropriate list (or creates a new (key,list) pair if none exists) so that the result would look like:
[(3,["a"]),(2,["c","b"]),(1,["a","e"])]
The following function does the trick:
fun store ((k,v), []) = [(k,[v])]
| store ((k,v), (m,vs)::items) = if k = m
then (m,v::vs)::items
else (m,vs)::store ((k,v) ,items);
Given a list of keys and a corresponding list of values, you could fold this last function over the corresponding zip of the keys and values:
fun group ks vs = foldl store [] (ListPair.zip(ks,vs));
For example, if
val original = [1, 2, 0, 1, 1, 2];
val elements = ["a","b","c","d","e","f"];
- group original elements;
val it = [(1,["e","d","a"]),(2,["f","b"]),(0,["c"])] : (int * string list) list
Note that you could sort this list according to the keys if so desired.
Finally -- if you just want the groups (reversed to match their original order in the list) the following works:
fun groups ks vs = map rev (#2 (ListPair.unzip (group ks vs)));
For example,
- groups original elements;
val it = [["a","d","e"],["b","f"],["c"]] : string list list
On Edit: if you want the final answer to be sorted according to the keys (as opposed to the order in which they appear) you could use #SimonShine 's idea and store the data in sorted order, or you could sort the output of the group function. Somewhat oddly, the SML Standard Basis Library lacks a built-in sort, but the standard implementations have their own sorts (and it is easy enough to write your own). For example, using SML/NJ's sort you could write:
fun sortedGroups ks vs =
let
val g = group ks vs
val s = ListMergeSort.sort (fn ((i,_),(j,_)) => i>j) g
in
map rev (#2 (ListPair.unzip s))
end;
Leading to the expected:
- sortedGroups original elements;
val it = [["c"],["a","d","e"],["b","f"]] : string list list
With the general strategy to first form a list of pairs (k, vs) where k is the value they are grouped by and vs is the elements, one could then extract the elements alone. Since John did this, I'll add two other things you can do:
Assume that original : int list, insert the pairs in sorted order:
fun group ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case Int.compare (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
This produces the same result as your example:
- val mappedList = group original elements;
> val mappedList = [["c"], ["a", "d", "e"], ["b", "f"]] : string list list
I'm a bit unsure if by "The actual content is not predictable." you also mean "The types of original and elements are not known." So:
Assume that original : 'a list and that some cmp : 'a * 'a -> order exists:
fun group cmp ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case cmp (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
Use a tree for storing pairs:
datatype 'a bintree = Empty | Node of 'a bintree * 'a * 'a bintree
(* post-order tree folding *)
fun fold f e Empty = e
| fold f e0 (Node (left, x, right)) =
let val e1 = fold f e0 right
val e2 = f (x, e1)
val e3 = fold f e2 left
in e3 end
fun group cmp ks vs =
let fun insert ((k, v), Empty) = Node (Empty, (k, [v]), Empty)
| insert (k1v as (k1, v), Node (left, k2vs as (k2, vs), right)) =
case cmp (k1, k2) of
LESS => Node (insert (k1v, left), k2vs, right)
| EQUAL => Node (left, (k2, v::vs), right)
| GREATER => Node (left, k2vs, insert (k1v, right))
fun extract ((k, vs), result) = rev vs :: result
in
fold extract [] (List.foldl insert Empty (ListPair.zip (ks, vs)))
end

OCaml : filter map and put the values into a list

I can filter my map by key :
module PairKeys =
struct
type t = string * string
let compare (x0,y0) (x1,y1) =
match String.compare x0 x1 with
| 0 -> String.compare y0 y1
| c -> c
end
module StringMap = Map.Make(PairKeys);;
....
let put_key_values_into_a_list (key_searched : string) =
StringMap.filter (fun key -> key = key_searched)
(* should return a list of the values in the filtered map *)
After that, I want to put the values into a list.
How can I do this in OCaml?
Map.Make documentation :
http://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.Make.html
Thanks
You can use bindings to retrieve the key/value pairs of a map and then further process them to extract the values. For example:
let put_key_values_into_a_list key_searched map =
MyMap.filter (fun key _ -> key = key_searched) map
|> MyMap.bindings |> List.split |> snd
We use List.split to convert a list of pairs into a pair of lists (one containing the keys, one the values) and then snd to extract the list of values.
Note also that filter takes a function with two arguments (the second of which gets ignored here).
Here is how I did it. I called fold after filter :
let put_key_values_into_a_list key_searched map =
MyMap.fold (fun _ i acc -> i::acc)
(MyMap.filter (fun (x,_) _ -> x = key_searched) map)
[]

Scala Filter List[Int] Which Exists in other List of Tuples

I've a two lists dest (contains:x) and points (x,y)
dest:List[Int] and Points:List[(Int,Int)]
I want to filter elements in the dest, if it exists in points (x==points._1) i
var newl:List[Int] = List()
for(x<-dest) if(!points.filter(_._1==x).isEmpty) newl=newl:+x
I feel like there must be a better concise way with exists but tuple making it complex. So whats the best way to do the above?
Here is a concise way:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val newl = dest.filter{d => points.exists(_._1 == d)} // returns List(1, 2)
The following is even better order of complexity wise:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val xs = points.map{_._1}.toSet
val newl = dest.filter(xs.contains(_))

Zipping on more than 2 lists in sml

I am having a problem with tail in my function.I would like to get rid of the tail but I cannot think of a way to do it.I will be happy if someone may assist me figure a way out.
fun Rollists (x::nil) = [x]
| Rollists (xs) =(map hd xs)::Rollists( map tl xs);
this function,is supposed to output elements from a given list list as pairs from each list,an larger version of ListPair.zip
Generates pairs from a list of lists based upon the first list:
fun generateTuples (lol) =
let
(* Treat the ListOfLists as a database
table, with the first
list being a key value column *)
val keys = hd(lol)
(* and the remaining columns being
additional fields in its tuple *)
val records = tl(lol)
(* Pairs the key with each column
in its record *)
fun formPairs (aKey,listOfRecords) =
if null listOfRecords
then []
else [aKey,hd(hd listOfRecords)]::
(formPairs(aKey,tl(listOfRecords)))
(* Pops a row's data fields from the record *)
fun chopLists (listOfRecords)=
if null listOfRecords
then []
else tl(hd(listOfRecords))::
(chopLists(tl(listOfRecords)))
in
(* Pass the first key value to formPairs
along with all the records. Then pop
the first row off the database and call
generateTuples on the remain *)
if null keys
then []
else generateTuples(tl(keys)::(chopLists(records)))
# formPairs(hd(keys),records)
end
Example:
val list1 = [0,1,2,3,4]
val list2 = [55,66,77,88,99]
val list3 = [10,11,12,13,14]
val list4 = [555,666,777,888,999]
val lols = [list1,list2,list3,list4]
- generateTuples(lols);
val it =
[[4,99],[4,14],[4,999],[3,88],[3,13],[3,888],[2,77],[2,12],[2,777],[1,66],
[1,11],[1,666],...] : int list list

How to merge two lists of tuples?

I have two lists in Scala, how to merge them such that the tuples are grouped together?
Is there an existing Scala list API which can do this or need I do it by myself?
Input:
List((a,4), (b,1), (c,1), (d,1))
List((a,1), (b,1), (c,1))
Expected output:
List((a,5),(b,2),(c,2),(d,1))
You can try the following one-line:
scala> ( l1 ++ l2 ).groupBy( _._1 ).map( kv => (kv._1, kv._2.map( _._2).sum ) ).toList
res6: List[(Symbol, Int)] = List(('a,5), ('c,2), ('b,2), ('d,1))
Where l1 and l2 are the lists of tuples you want merge.
Now, the breakdown:
(l1 ++ l2) you just concatenate both lists
.groupBy( _._1) you group all tuples by their first element. You will receive a Map with
the first element as key and lists of tuples starting with this element as values.
.map( kv => (kv._1, kv._2.map( _._2).sum ) ) you make a new map, with similar keys, but the values are the sum of all second elements.
.toList you convert the result back to a list.
Alternatively, you can use pattern matching to access the tuple elements.
( l1 ++ l2 ).groupBy( _._1 ).map{
case (key,tuples) => (key, tuples.map( _._2).sum )
}.toList
Alternatively you can also use mapValues to shorten the code.
mapValues, as you can probably guess, allows you to re-map just the value for each (key, value) pair in the Map created by groupBy.
In this case the function passed to mapValues reduces each (Char, Int) tuple to just the Int then sums the resulting List of Ints.
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList
If the order of the output list needs to follow your example, just add sorted which relies on an Ordering[(Char, Int)] implicit instance.
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted
If you can assume that both List[(A,B)] are ordered according to Ordering[A], you could write something like:
def mergeLists[A,B](one:List[(A,B)], two:List[(A,B)])(op:(B,B)=>B)(implicit ord:Ordering[A]): List[(A,B)] = (one,two) match {
case (xs, Nil) => xs
case (Nil, ys) => ys
case((a,b)::xs,(aa,bb)::ys) =>
if (a == aa) (a, op(b,bb)) :: mergeLists(xs,ys)(op)(ord)
else if (ord.lt(a,aa)) (a, b) :: mergeLists(xs, (aa,bb)::ys)(op)(ord)
else (aa, bb) :: mergeLists((a,b) :: xs, ys)(op)(ord)
}
Unfortunately this isn't tail recursive.
Using foldLeft and toMap:
Get a map out of one list, and iterate through the second list. We upsert entries into the map.
l1.foldLeft(l2.toMap)((accumulator, tuple) =>
accumulator + (tuple._1 -> (accumulator.getOrElse(tuple._1, 0) + tuple._2))
).toList
results into:
List((Symbol(a),5), (Symbol(b),2), (Symbol(c),2), (Symbol(d),1))
Explanation:
l2.toMap converts List((a,1), (b,1), (c,1)) into immutable.Map(a->1, b->1, c->1)
foldLeft iterates through each tuple of list#1 l1.
(a,4) of list1 is added to the generated map, resulting to immutable.Map(a->1+4, b->1, c->1)
(b,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->1)
(c,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->2)
(d,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->2, d->1)
toList converts the map back to the original input form, i.e. List[(Symbol, Int)]