How to merge tuples by same elements in Scala - list

For example, if I have the following tuples:
(1, "a", "l")
(1, "a", "m")
(1, "a", "n")
I want to merge them like this:
(1, "a", List("l", "m", "n"))
In my case, the lists are a result from an inner join using Slick.
So, the first and second elements (1 and "a") should be the same.
If somebody knows how to merge like that in case of using Slick, let me know please.
Or more generally, the way to merge tuples with inner lists by the same elements.
(1, "a", "l")
(1, "a", "m")
(1, "b", "n")
(1, "b", "o")
// to like this
List( (1, "a", List("l", "m")), (1, "b", List("n", "o")) )

How about:
val l = ??? // Your list
val groups = l groupBy { case (a, b, c) => (a,b) }
val tups = groups map { case ((a,b), l) => (a,b,l.map(_._3)) }
tups.toList

You could try foldRight
val l = List((1, "a", "l"), (1, "a", "m"), (1, "a", "n"), (1, "b", "n"), (1, "b", "o"))
val exp = List((1, "a", List("l", "m", "n")), (1, "b", List("n", "o")))
val result = l.foldRight(List.empty[(Int, String, List[String])]) {
(x, acc) =>
val (n, s1, s2) = x
acc match {
case (n_, s1_, l_) :: t if (n == n_ && s1 == s1_) =>
(n_, s1_, (s2 :: l_)) :: t
case _ =>
(n, s1, List(s2)) :: acc
}
}
println(result)
println(result == exp)
Update
If the input list is not sorted:
val result = l.sorted.foldRight(...)

Related

How to sort a list of tuples using both key and value in OCaml?

I have a list of tuples like (1, "a"), (2, "b"), (3, "c") and I want to sort them like
1 a
2 b
3 b
Where the numerical order takes precedence first and then the alphabetical order. Like if I have
(1, "bob"), (1, "cat")
It would look like
1 bob
1 cat
Here's what I'm trying
let mylist = List.sort (fun (c1, s1) (c2, s2) -> Pervasives.compare c2 c1 ) -> Pervasives.compare s1 s2) mysortedlist
But this obviously is syntactically incorrect.
Here's the way it is if it only sorted based on numerical order
let mylist = List.sort (fun (c1, _) (c2, _) -> compare c2 c1) mylist
Also these let statements are part of a much larger block of let and in statements, I'm just focusing on this specific piece.
EDIT: Also I would like to do this in reverse order for the alphabetical order.
For example, (1, "bob"), (1, "cat") (0, "zebra") should look like
0 zebra
1 bob
1 cat
This is pretty straightforward. The function you pass to List.sort just needs to compare the values if the keys are the same.
List.sort
(fun (k1, v1) (k2, v2) ->
if k1 = k2 then compare v1 v2
else compare k1 k2)
[(1, "B"); (2, "C"); (2, "A")]
And the result is:
[(1, "B"); (2, "A"); (2, "C")]
Alternatively, since it saves us from doing both an equality check on k1 and k2 and possibly running compare on them:
List.sort
(fun (k1, v1) (k2, v2) ->
match compare k1 k2 with
| 0 -> compare v1 v2
| c -> c)
[(1, "B"); (2, "C"); (2, "A")]

How to flatten named tuples in a tuple in julia?

I have a Tuple of elements, some of which are NamedTuples. I would like to flatten the NamedTuples like so:
julia> nt = (a="a", b="b")
(a = "a", b = "b")
julia> t = (1, 2, 3, nt)
(1, 2, 3, (a = "a", b = "b"))
julia> res = tuple(1, 2, 3, nt...)
(1, 2, 3, "a", "b")
How to do this programmatically? I tried the following:
julia> exprs = [x isa NamedTuple ? Meta.parse("$x...") : x for x in t]
4-element Array{Any,1}:
1
2
3
:((a = "a", b = "b")...)
julia> res = tuple(eval(ex) for ex in exprs)
(Base.Generator{Array{Any,1},typeof(eval)}(Base.MainInclude.eval, Any[1, 2, 3, :((a = "a", b = "b")...)]),)
But it doesn't quite give what I would like:
(1, 2, 3, "a", "b")
The simplest way to do it would be to write:
julia> Tuple(Iterators.flatten((1,2,3, (a="a",b="b"))))
(1, 2, 3, "a", "b")
this has a downside that it is type unstable and that it will flatten all iterables (not only NamedTuples).
If you want only NamedTuples flattened then something like this could be used:
julia> Tuple(reduce((a,b) -> (b isa NamedTuple ? append! : push!)(a, b), (1,2,3, (a="a",b="b")), init=[]))
(1, 2, 3, "a", "b")
(still it will be type unstable)
If you want something type stable you can use recursion e.g. like this:
flatten() = ()
flatten(a::NamedTuple) = Tuple(a)
flatten(a) = (a,)
flatten(a::NamedTuple, b...) = tuple(a..., flatten(b...)...)
flatten(a, b...) = tuple(a, flatten(b...)...)
flatten_tuple(x::Tuple) = flatten(x...)
and now you have:
julia> flatten_tuple((1,2,3, (a="a",b="b")))
(1, 2, 3, "a", "b")
julia> #code_warntype flatten_tuple((1,2,3, (a="a",b="b")))
Variables
#self#::Core.Compiler.Const(flatten_tuple, false)
x::Tuple{Int64,Int64,Int64,NamedTuple{(:a, :b),Tuple{String,String}}}
Body::Tuple{Int64,Int64,Int64,String,String}
1 ─ %1 = Core._apply_iterate(Base.iterate, Main.flatten, x)::Tuple{Int64,Int64,Int64,String,String}
└── return %1

Scala pairs using map and flatten

I'm working on a problem to take two lists, for example (1,2,3) and (a,b) and return a list ((1,a)(1,b)(2,a)(2,b)(3,a)(3,b)) using only map and flatten.
This problem requires me to define a function as follows:
def product[A](xs: List[A], ys: List[A])= {
And within this function get the result. I'm rather new to Scala and I am used to languages like python and java.
I've gotten this far:
def product[A](xs: List[A], ys: List[A])= {
for(y <- ys){
println(xs.map(x=> (x,y)))
}
}
This will return something like this:
List((1,a), (2,a), (3,a))
List((1,b), (2,b), (3,b))
I'm not sure how can combine these lists now. In python I would do something like create a new list variable, append both of these lists to that list, and then flatten it so I would have one list. However, I'm rather confused by scala as it seems as if I am not allowed to define a new variable within a function. How can I combine these lists and flatten them at this point?
Product List using only map and flatten
val nums = List(1, 2, 3)
val chars = List('a', 'b')
nums.map { a => chars.map { b => (a, b) } }.flatten
Scala REPL
scala> nums.map(a => chars.map(b => (a, b)))
res5: List[List[(Int, Char)]] = List(List((1, 'a'), (1, 'b')), List((2, 'a'), (2, 'b')), List((3, 'a'), (3, 'b')))
scala> nums.map(a => chars.map(b => (a, b))).flatten
res6: List[(Int, Char)] = List((1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'))
Product List can be built using flatMap and map combination
nums.flatMap { a => chars.map { b => (a, b) }}
Product List can also be built using for comprehension
for {a <- nums; b <- chars} yield (a, b)
Scala REPL
scala> val nums = List(1, 2, 3)
val nums: List[Int] = List(1, 2, 3)
scala> val chars = List('a', 'b')
chars: List[Char] = List('a', 'b')
scala> nums.flatMap { a => chars.map { b => (a, b) }}
res2: List[(Int, Char)] = List((1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'))
scala> for {a <- nums; b <- chars} yield (a, b)
res3: List[(Int, Char)] = List((1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'))
You can solve it using for comprehension. It's actually a syntactic sugar for map and flatMap:
def product[A](xs: List[A], ys: List[A])= {
for {
x <- xs
y <- ys
} yield {
x -> y
}
}
For-comprehensions is the Scala idiomatic way to achieve this. It's more readable and maintainable, and the underlying operations are still map and flatMap. In fact, even for types that are not collections, but still have map and flatMap, it's common to use for comprehensions (Futures, Options, Trys, etc.)
Edit
If you want to continue with your solution and save the lists and combine them, you'll have to drop the println and add a yield, and then flatten the main list that was created:
def product[A](xs: List[A], ys: List[A]) = {
for (y <- ys) yield {
xs.map(x => (x, y))
}
}
val res = product(List(1, 2, 3), List("a", "b"))
println(res.flatten)
val ls1 = List(1,2,3)
val ls2 = List('a','b')
def product[A](xs: List[A], ys: List[A])= xs.map(x => ys.map((x,_))).flatten
product(ls1,ls2)

Scala: find all chains in a list

Say I have a list of items:
Seq(A, B, B, B, B, G, G, S, S, S, B, A, G)
And I want to find all the chains and get a sequence of them like so:
Seq(Seq(A), Seq(B, B, B, B), Seq(G, G), Seq(S, S, S), Seq(B), Seq(A), Seq(G))
I want to maintain order, and use a custom comparison function to decide if two objects are the "same". I'm thinking a fold or a scan may be what I need, but I'm having trouble coming up with the exact case. I'm using Scala.
EDIT: I've modified the answer from that similar question to get this:
def collapse(input: Seq[Stmt]): Seq[Seq[Stmt]] = {
val (l, r) = input.span(_.getClass == input.head.getClass)
l :: collapse(r)
}
Cleaner solution:
def pack[T](input: List[T]): List[List[T]] =
input.foldRight(Nil : List[List[T]]) ((e, accu) => accu match {
case Nil => List(List(e))
case curList#(h :: t) if e == h => List(e) :: curList
case curList#(h :: t) => List(List(e)) ::: curList
})
Not using any library functions (ugly):
def pack[T](input: List[T]): List[List[T]] = {
def packWithPrevious(remaining: List[T])(previous: List[T]): List[List[T]] =
remaining match {
case List() => List(previous)
case head :: tail =>
val nextIter = packWithPrevious(tail)(_)
previous match {
case List() => nextIter(List(head))
case prevHead :: _ =>
if (head != prevHead)
previous :: nextIter(List(head))
else
nextIter(head :: previous)
}
}
packWithPrevious(input)(List())
}
scala> val s = List('A', 'B', 'B', 'B', 'B', 'G', 'G', 'S', 'S', 'S', 'B', 'A', 'G')
s: List[Char] = List(A, B, B, B, B, G, G, S, S, S, B, A, G)
scala> pack(s)
res2: List[List[Char]] = List(List(A), List(B, B, B, B), List(G, G), List(S, S, S), List(B), List(A), List(G))
Source: https://github.com/izmailoff/scala-s-99/blob/master/src/main/scala/s99/p09/P09.scala
Test: https://github.com/izmailoff/scala-s-99/blob/master/src/test/scala/s99/p09/P09Suite.scala
Similar to existing answers but I find using a partial function directly in foldLeft as a clean solution:
val s = Seq("A", "B", "B", "B", "B", "G", "G", "S", "S", "S", "B", "A", "G")
s.foldLeft(Seq[Seq[String]]()) {
case (Seq(), item) => Seq(Seq(item))
case (head::tail, item) if head.contains(item) => (item +: head) +: tail
case (seq, item) => Seq(item) +: seq
}.reverse
res0: Seq[Seq[String]] = List(List(A), List(B, B, B, B), List(G, G), List(S, S, S), List(B), List(A), List(G))
Consider following solution:
seq.foldLeft(List(List(seq.head))) { case (acc,item)=>
if(acc.head.head==item) (item::acc.head)::acc.tail else List(item)::acc
}.reverse
seq may be empty, so:
seq.foldLeft(List(seq.headOption.toList)) { case (acc,item)=>
if(acc.head.head==item) (item::acc.head)::acc.tail else List(item)::acc
}.reverse
I thought groupBy would be helpful here, but my solution got slightly awkward:
val seq = Seq("A", "B", "B", "B", "B", "G", "G", "S", "S", "S", "B", "A", "G")
val parts = {
var lastKey: Option[(Int, String)] = None
seq.groupBy(s => {
lastKey = lastKey.map((p: (Int, String)) =>
if (p._2.equalsIgnoreCase(s)) p else (p._1 + 1, s)) orElse Some((0, s))
lastKey.get
}).toSeq.sortBy(q => q._1).flatMap(q => q._2)
}
(using equalsIgnoreCase as example for a comparision function)

merge (int * string) list ocaml

I have this function:
let encode list =
let rec aux count acc = function
| [] -> [] (* Caso a lista esteja vazia*)
| [x] -> (count+1, x) :: acc
| a :: (b :: _ as t) ->
if a = b then aux (count + 1) acc t
else aux 0 ((count+1,a) :: acc) t in
List.rev (aux 0 [] list)
;;
and with this input:
let test = encode ["a";"a";"a";"a";"b";"f";"f";"c";"c";"a";"a";"d";"e";"e";"e";"e"];;
And i have this Output:
val test : (int * string) list =
[(4, "a"); (1, "b"); (2, "f"); (2, "c"); (2, "a"); (1, "d"); (4, "e")]
but the "a" is a repeated and "f" need to be at final!
I need a output like:
val test : (int * string) list =
[(6, "a"); (1, "b"); (2, "c"); (1, "d"); (4, "e"); (2, "f")]
Can anybody help, please?! Thanks!
You are counting repeated adjacent values, so-called run-length encoding. It appears you want to count occurrences across the whole input. You can either sort the input beforehand, or you can use a more complicated data structure (such as a Map) to keep track of your counts.
Something like this:
let encode xs =
let f acc x =
let n = try M.find x acc with Not_found -> 0 in
M.add x (n+1) acc in
let ans = (List.fold_left f M.empty) xs in
M.bindings ans ;;
# encode ["a";"a";"a";"a";"b";"f";"f";"c";"c";"a";"a";"d";"e";"e";"e";"e"];;
- : (M.key * int) list =
[("a", 6); ("b", 1); ("c", 2); ("d", 1); ("e", 4); ("f", 2)]