How can I find repeated items in a Scala List? - scala
I have a Scala List that contains some repeated numbers. I want to count the number of times a specific number will repeat itself. For example:
val list = List(1,2,3,3,4,2,8,4,3,3,5)
val repeats = list.takeWhile(_ == List(3,3)).size
And the val repeats would equal 2.
Obviously the above is pseudo-code and takeWhile will not find two repeated 3s since _ represents an integer. I tried mixing both takeWhile and take(2) but with little success. I also referred code from How to find count of repeatable elements in scala list but it appears the author is looking to achieve something different.
Thanks for your help.
This will work in this case:
val repeats = list.sliding(2).count(_.forall(_ == 3))
The sliding(2) method gives you an iterator of lists of elements and successors and then we just count where these two are equal to 3.
Question is if it creates the correct result to List(3, 3, 3)? Do you want that to be 2 or just 1 repeat.
val repeats = list.sliding(2).toList.count(_==List(3,3))
and more generally the following code returns tuples of element and repeats value for all elements:
scala> list.distinct.map(x=>(x,list.sliding(2).toList.count(_.forall(_==x))))
res27: List[(Int, Int)] = List((1,0), (2,0), (3,2), (4,0), (8,0), (5,0))
which means that the element '3' repeats 2 times consecutively at 2 places and all others 0 times.
and also if we want element repeats 3 times consecutively we just need to modify the code as follows:
list.distinct.map(x=>(x,list.sliding(3).toList.count(_.forall(_==x))))
in SCALA REPL:
scala> val list = List(1,2,3,3,3,4,2,8,4,3,3,3,5)
list: List[Int] = List(1, 2, 3, 3, 3, 4, 2, 8, 4, 3, 3, 3, 5)
scala> list.distinct.map(x=>(x,list.sliding(3).toList.count(_==List(x,x,x))))
res29: List[(Int, Int)] = List((1,0), (2,0), (3,2), (4,0), (8,0), (5,0))
Even sliding value can be varied by defining a function as:
def repeatsByTimes(list:List[Int],n:Int) =
list.distinct.map(x=>(x,list.sliding(n).toList.count(_.forall(_==x))))
Now in REPL:
scala> val list = List(1,2,3,3,4,2,8,4,3,3,5)
list: List[Int] = List(1, 2, 3, 3, 4, 2, 8, 4, 3, 3, 5)
scala> repeatsByTimes(list,2)
res33: List[(Int, Int)] = List((1,0), (2,0), (3,2), (4,0), (8,0), (5,0))
scala> val list = List(1,2,3,3,3,4,2,8,4,3,3,3,2,4,3,3,3,5)
list: List[Int] = List(1, 2, 3, 3, 3, 4, 2, 8, 4, 3, 3, 3, 2, 4, 3, 3, 3, 5)
scala> repeatsByTimes(list,3)
res34: List[(Int, Int)] = List((1,0), (2,0), (3,3), (4,0), (8,0), (5,0))
scala>
We can go still further like given a list of integers and given a maximum number
of consecutive repetitions that any of the element can occur in the list, we may need a list of 3-tuples representing (the element, number of repetitions of this element, at how many places this repetition occurred). this is more exhaustive information than the above. Can be achieved by writing a function like this:
def repeats(list:List[Int],maxRep:Int) =
{ var v:List[(Int,Int,Int)] = List();
for(i<- 1 to maxRep)
v = v ++ list.distinct.map(x=>
(x,i,list.sliding(i).toList.count(_.forall(_==x))))
v.sortBy(_._1) }
in SCALA REPL:
scala> val list = List(1,2,3,3,3,4,2,8,4,3,3,3,2,4,3,3,3,5)
list: List[Int] = List(1, 2, 3, 3, 3, 4, 2, 8, 4, 3, 3, 3, 2, 4, 3, 3, 3, 5)
scala> repeats(list,3)
res38: List[(Int, Int, Int)] = List((1,1,1), (1,2,0), (1,3,0), (2,1,3),
(2,2,0), (2,3,0), (3,1,9), (3,2,6), (3,3,3), (4,1,3), (4,2,0), (4,3,0),
(5,1,1), (5,2,0), (5,3,0), (8,1,1), (8,2,0), (8,3,0))
scala>
These results can be understood as follows:
1 times the element '1' occurred at 1 places.
2 times the element '1' occurred at 0 places.
............................................
............................................
.............................................
2 times the element '3' occurred at 6 places..
.............................................
3 times the element '3' occurred at 3 places...
............................................and so on.
Thanks to Luigi Plinge I was able to use methods in run-length encoding to group together items in a list that repeat. I used some snippets from this page here: http://aperiodic.net/phil/scala/s-99/
var n = 0
runLengthEncode(totalFrequencies).foreach{ o =>
if(o._1 > 1 && o._2==subjectNumber) n+=1
}
n
The method runLengthEncode is as follows:
private def pack[A](ls: List[A]): List[List[A]] = {
if (ls.isEmpty) List(List())
else {
val (packed, next) = ls span { _ == ls.head }
if (next == Nil) List(packed)
else packed :: pack(next)
}
}
private def runLengthEncode[A](ls: List[A]): List[(Int, A)] =
pack(ls) map { e => (e.length, e.head) }
I'm not entirely satisfied that I needed to use the mutable var n to count the number of occurrences but it did the trick. This will count the number of times a number repeats itself no matter how many times it is repeated.
If you knew your list was not very long you could do it with Strings.
val list = List(1,2,3,3,4,2,8,4,3,3,5)
val matchList = List(3,3)
(matchList.mkString(",")).r.findAllMatchIn(list.mkString(",")).length
From you pseudocode I got this working:
val pairs = list.sliding(2).toList //create pairs of consecutive elements
val result = pairs.groupBy(x => x).map{ case(x,y) => (x,y.size); //group pairs and retain the size, which is the number of occurrences.
result will be a Map[List[Int], Int] so you can the count number like:
result(List(3,3)) // will return 2
I couldn't understand if you also want to check lists of several sizes, then you would need to change the parameter to sliding to the desired size.
def pack[A](ls: List[A]): List[List[A]] = {
if (ls.isEmpty) List(List())
else {
val (packed, next) = ls span { _ == ls.head }
if (next == Nil) List(packed)
else packed :: pack(next)
}
}
def encode[A](ls: List[A]): List[(Int, A)] = pack(ls) map { e => (e.length, e.head) }
val numberOfNs = list.distinct.map{ n =>
(n -> list.count(_ == n))
}.toMap
val runLengthPerN = runLengthEncode(list).map{ t => t._2 -> t._1}.toMap
val nRepeatedMostInSuccession = runLengthPerN.toList.sortWith(_._2 <= _._2).head._1
Where runLength is defined as below from scala's 99 problems problem 9 and scala's 99 problems problem 10.
Since numberOfNs and runLengthPerN are Maps, you can get the population count of any number in the list with numberOfNs(number) and the length of the longest repitition in succession with runLengthPerN(number). To get the runLength, just compute as above with runLength(list).map{ t => t._2 -> t._1 }.
Related
Compare two list and get the index of same elements
val a = List(1,1,1,0,0,2) val b = List(1,0,3,2) I want to get the List of indices of elements of "List b" which are existing in "List a". Here output to be List(0,1,3) I tried this for(x <- a.filter(b.contains(_))) yield a.indexOf(x)) Sorry. I missed this. The list size may vary. Edited the Lists Is there a better way to do this?
If you want a result of indices, it's often useful to start with indices. b.indices.filter(a contains b(_)) REPL tested. scala> val a = List(1,1,1,0,0,2) a: List[Int] = List(1, 1, 1, 0, 0, 2) scala> val b = List(1,0,3,2) b: List[Int] = List(1, 0, 3, 2) scala> b.indices.filter(a contains b(_)) res0: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3)
val result = (a zip b).zipWithIndex.flatMap { case ((aItem, bItem), index) => if(aItem == bItem) Option(index) else None } a zip b will return all elements from a that have a matching pair in b. For example, if a is longer, like in your example, the result would be List((1,1),(1,0),(1,3),(0,2)) (the list will be b.length long). Then you need the index also, that's zipWithIndex. Since you only want the indexes, you return an Option[Int] and flatten it.
You can use indexed for for this: for{ i <- 0 to b.length-1 if (a contains b(i)) } yield i
scala> for(x <- b.indices.filter(a contains b(_))) yield x; res27: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3)
Here is another option: scala> val a = List(1,1,1,0,0,2) a: List[Int] = List(1, 1, 1, 0, 0, 2) scala> val b = List(1,0,3,2) b: List[Int] = List(1, 0, 3, 2) scala> b.zipWithIndex.filter(x => a.contains(x._1)).map(x => x._2) res7: List[Int] = List(0, 1, 3) I also want to point out that your original idea of: Finding elements in b that are in a and then getting indices of those elements would not work, unless all elements in b contained in a are unique, indexOf returns index of the first element. Just heads up.
Scala List Operation
Given a List of Int and variable X of Int type . What is the best in Scala functional way to retain only those values in the List (starting from beginning of list) such that sum of list values is less than equal to variable.
This is pretty close to a one-liner: def takeWhileLessThan(x: Int)(l: List[Int]): List[Int] = l.scan(0)(_ + _).tail.zip(l).takeWhile(_._1 <= x).map(_._2) Let's break that into smaller pieces. First you use scan to create a list of cumulative sums. Here's how it works on a small example: scala> List(1, 2, 3, 4).scan(0)(_ + _) res0: List[Int] = List(0, 1, 3, 6, 10) Note that the result includes the initial value, which is why we take the tail in our implementation. scala> List(1, 2, 3, 4).scan(0)(_ + _).tail res1: List[Int] = List(1, 3, 6, 10) Now we zip the entire thing against the original list. Taking our example again, this looks like the following: scala> List(1, 2, 3, 4).scan(0)(_ + _).tail.zip(List(1, 2, 3, 4)) res2: List[(Int, Int)] = List((1,1), (3,2), (6,3), (10,4)) Now we can use takeWhile to take as many values as we can from this list before the cumulative sum is greater than our target. Let's say our target is 5 in our example: scala> res2.takeWhile(_._1 <= 5) res3: List[(Int, Int)] = List((1,1), (3,2)) This is almost what we want—we just need to get rid of the cumulative sums: scala> res2.takeWhile(_._1 <= 5).map(_._2) res4: List[Int] = List(1, 2) And we're done. It's worth noting that this isn't very efficient, since it computes the cumulative sums for the entire list, etc. The implementation could be optimized in various ways, but as it stands it's probably the simplest purely functional way to do this in Scala (and in most cases the performance won't be a problem, anyway).
In addition to Travis' answer (and for the sake of completeness), you can always implement these type of operations as a foldLeft: def takeWhileLessThanOrEqualTo(maxSum: Int)(list: Seq[Int]): Seq[Int] = { // Tuple3: the sum of elements so far; the accumulated list; have we went over x, or in other words are we finished yet val startingState = (0, Seq.empty[Int], false) val (_, accumulatedNumbers, _) = list.foldLeft(startingState) { case ((sum, accumulator, finished), nextNumber) => if(!finished) { if (sum + nextNumber > maxSum) (sum, accumulator, true) // We are over the sum limit, finish else (sum + nextNumber, accumulator :+ nextNumber, false) // We are still under the limit, add it to the list and sum } else (sum, accumulator, finished) // We are in a finished state, just keep iterating over the list } accumulatedNumbers } This only iterates over the list once, so it should be more efficient, but is more complicated and requires a bit of reading code to understand.
I will go with something like this, which is more functional and should be efficient. def takeSumLessThan(x:Int,l:List[Int]): List[Int] = (x,l) match { case (_ , List()) => List() case (x, _) if x<= 0 => List() case (x, lh :: lt) => lh :: takeSumLessThan(x-lh,lt) } Edit 1 : Adding tail recursion and implicit for shorter call notation import scala.annotation.tailrec implicit class MyList(l:List[Int]) { def takeSumLessThan(x:Int) = { #tailrec def f(x:Int,l:List[Int],acc:List[Int]) : List[Int] = (x,l) match { case (_,List()) => acc case (x, _ ) if x <= 0 => acc case (x, lh :: lt ) => f(x-lh,lt,acc ++ List(lh)) } f(x,l,Nil) } } Now you can use this like List(1,2,3,4,5,6,7,8).takeSumLessThan(10)
How to sum adjacent elements in scala
I want to sum adjacent elements in scala and I'm not sure how to deal with the last element. So I have a list: val x = List(1,2,3,4) And I want to sum adjacent elements using indices and map: val size = x.indices.size val y = x.indices.map(i => if (i < size - 1) x(i) + x(i+1)) The problem is that this approach creates an AnyVal elemnt at the end: res1: scala.collection.immutable.IndexedSeq[AnyVal] = Vector(3, 5, 7, ()) and if I try to sum the elements or another numeric method of the collection, it doesn't work: error: could not find implicit value for parameter num: Numeric[AnyVal] I tried to filter out the element using: y diff List(Unit) or y diff List(AnyVal) but it doesn't work. Is there a better approach in scala to do this type of adjacent sum without using a foor loop?
For a more functional solution, you can use sliding to group the elements together in twos (or any number of them), then map to their sum. scala> List(1, 2, 3, 4).sliding(2).map(_.sum).toList res80: List[Int] = List(3, 5, 7) What sliding(2) will do is create an intermediate iterator of lists like this: Iterator( List(1, 2), List(2, 3), List(3, 4) ) So when we chain map(_.sum), we will map each inner List to it's own sum. toList will convert the Iterator back into a List.
You can try pattern matching and tail recursion also. import scala.annotation.tailrec #tailrec def f(l:List[Int],r :List[Int]=Nil):List[Int] = { l match { case x :: xs :: xss => f(l.tail, r :+ (x + xs)) case _ => r } } scala> f(List(1,2,3,4)) res4: List[Int] = List(3, 5, 7)
With a for comprehension by zipping two lists, the second with the first item dropped, for ( (a,b) <- x zip x.drop(1) ) yield a+b which results in List(3, 5, 7)
Is there a Round Robin/Circular Queue available in Scala Collections
Is there a Round Robin Queue available in Scala Collections? I need to repeatedly iterate a list that circles through itself val x = new CircularList(1,2,3,4) x.next (returns 1) x.next (returns 2) x.next (returns 3) x.next (returns 4) x.next (returns 1) x.next (returns 2) x.next (returns 3) ... and so on
It's pretty easy to roll your own with continually and flatten: scala> val circular = Iterator.continually(List(1, 2, 3, 4)).flatten circular: Iterator[Int] = non-empty iterator scala> circular.take(17).mkString(" ") res0: String = 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 There's also a continually method on Stream—just be careful not to hold onto a reference to the head of the stream if you're going to be generating lots of elements.
You can very easily create a circular list using a Stream. scala> val l = List(1, 2, 3, 4).toStream l: scala.collection.immutable.Stream[Int] = Stream(1, ?) scala> def b: Stream[Int] = l #::: b b: Stream[Int] scala> b.take(20).toList res2: List[Int] = List(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4) Edit: you want to make sure to define the repeated part beforehand, once and only once, to avoid blowing the heap (structural sharing in Stream). As in: def circular[A](a: Seq[A]): Stream[A] = { val repeat = a.toStream def b: Stream[A] = repeat #::: b b }
Version more concentrated on getting new element on every execution. val getNext: () => Int = { def b: Stream[Int] = List(1, 2, 3, 4).toStream #::: b var cyclicIterator: Stream[Int] = b () => { val tail = cyclicIterator.tail val result = tail.head cyclicIterator = tail result } } // could be written more sexy? In your problem you can use it like: for(i <- 1 to 10) yield getNext()
This is ugly in having an external mutable index, but it does do what's requested: scala> var i = 0 scala> val ic4 = Iterator.continually { val next = IndexedSeq(1, 2, 3, 4)(i % 4); i += 1; next } i: Int = 0 ic4: Iterator[Int] = non-empty iterator scala> ic4 take 10 foreach { i => printf("ic4.next=%d%n", i) } ic4.next=1 ic4.next=2 ic4.next=3 ic4.next=4 ic4.next=1 ic4.next=2 ic4.next=3 ic4.next=4 ic4.next=1 ic4.next=2 At least it illustrates Iterator.continually. There is also Stream.continually, which has the same signature.
Replace element in List with scala
How do you replace an element by index with an immutable List. E.g. val list = 1 :: 2 ::3 :: 4 :: List() list.replace(2, 5)
If you want to replace index 2, then list.updated(2,5) // Gives 1 :: 2 :: 5 :: 4 :: Nil If you want to find every place where there's a 2 and put a 5 in instead, list.map { case 2 => 5; case x => x } // 1 :: 5 :: 3 :: 4 :: Nil In both cases, you're not really "replacing", you're returning a new list that has a different element(s) at that (those) position(s).
In addition to what has been said before, you can use patch function that replaces sub-sequences of a sequence: scala> val list = List(1, 2, 3, 4) list: List[Int] = List(1, 2, 3, 4) scala> list.patch(2, Seq(5), 1) // replaces one element of the initial sequence res0: List[Int] = List(1, 2, 5, 4) scala> list.patch(2, Seq(5), 2) // replaces two elements of the initial sequence res1: List[Int] = List(1, 2, 5) scala> list.patch(2, Seq(5), 0) // adds a new element res2: List[Int] = List(1, 2, 5, 3, 4)
You can use list.updated(2,5) (which is a method on Seq). It's probably better to use a scala.collection.immutable.Vector for this purpose, becuase updates on Vector take (I think) constant time.
You can use map to generate a new list , like this : # list res20: List[Int] = List(1, 2, 3, 4, 4, 5, 4) # list.map(e => if(e==4) 0 else e) res21: List[Int] = List(1, 2, 3, 0, 0, 5, 0)
It can also be achieved using patch function as scala> var l = List(11,20,24,31,35) l: List[Int] = List(11, 20, 24, 31, 35) scala> l.patch(2,List(27),1) res35: List[Int] = List(11, 20, 27, 31, 35) where 2 is the position where we are looking to add the value, List(27) is the value we are adding to the list and 1 is the number of elements to be replaced from the original list.
If you do a lot of such replacements, it is better to use a muttable class or Array.
following is a simple example of String replacement in scala List, you can do similar for other types of data scala> val original: List[String] = List("a","b") original: List[String] = List(a, b) scala> val replace = original.map(x => if(x.equals("a")) "c" else x) replace: List[String] = List(c, b)