I'm would like to create generic (invariant) method in Scala which copies elements from source list to destination list. In Java there is copy method in java.util.Collections (see http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#copy%28java.util.List,%20java.util.List%29). I know in Scala List is immutable object so i would like to create and return new list.
I've written the following code:
def copy[T](dest:List[T], src:List[T]):List[T] = {
if(dest.length<src.length) throw new Exception("IndexOutOfBoundsException")
else if(src==Nil) dest
else {
var ret = dest
ret = dest.updated(0, src.first)
var i=1
val f:(T=>Unit) = a => {
if(i<src.length) ret=ret.updated(i, src(i))
i+=1
()
}
dest.foreach(f)
ret
}
}
But I think it could be written better. Could you help me to write better code? Thanks in advance.
EDITED: Maybe I expressed unclear what I want to do. I have two lists (scala.collection.immutable.List), e.g. src (length=x) and dest(length=y>=x). I would like to replace first x elements of dest list with elements from src list.
Do you mean scala.collection.immutable.List? It is immutable. No need to copy them. Immutable means that nothing can change it, so you can use it in different threads.
Generic way of creating collections in scala is builder. You can get one from CanBuildFrom object. Alternatively you can get it from genericBuilder method of collection instance.
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> val b = list.genericBuilder[Int]
b: scala.collection.mutable.Builder[Int,List[Int]] = ListBuffer()
scala> list.foreach{ b += _ }
scala> val result = b.result // this code is useless. `val result = list` is enough
result: List[Int] = List(1, 2, 3)
If you want to create new collection of different type based on existing collection, you can use collection.breakOut methid like this:
scala> val list = List('a', 'b', 'c')
list: List[Char] = List(a, b, c)
scala> val result: String = list.map{identity}(collection.breakOut)
result: String = abc
Upd
require(src.length <= dest.length, "IndexOutOfBoundsException")
src ++ dest.drop(src.length)
If you want to get an updated list you can use map on your list. Map works by applying a function to each element in the list, and returning updated list.
http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/
You are thinking far too procedurally, say what you want not how to do it...
how about:
val src = List(1,2,3)
val dest = src map {x => x}
if you really want to make a function of it
def copy[T](src: List[T]): List[T] = src map {x => x}
in response to OP's update:(which has also been proposed by others)
def copy[T](src: List[T], dest: List[T]): List[T] = src ++ dest.drop(src.length)
You could use:
if(dest.length <= src.length) dest ::: src.drop(dest.length)
else dest.dropRight(dest.length - src.length) //or throw exception...
Maybe you want something like
def copy[T](dest: Seq[T], src: Seq[T]): Seq[T] = {
require(dest.length >= src.length)
src ++ (dest drop src.length)
}
I generalized to Seqs, but it works on Lists, of course
The require method throws IllegalArgumentException if not fulfilled at runtime
Then you need only append the last (y-x) elements of the destination list to to the source list (where x = src.length; y = dest.length)
You do this by dropping x elements from dest and appending the remaining to src.
This is what you get from the REPL
scala> val src = List(1, 2, 3, 4)
src: List[Int] = List(1, 2, 3, 4)
scala> val dst = List(10, 20)
dst: List[Int] = List(10, 20)
scala> val dst2 = List(10, 20, 30, 40, 50, 60)
dst2: List[Int] = List(10, 20, 30, 40, 50, 60)
scala> copy(dst, src)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
at .copy(<console>:8)
at .<init>(<console>:11)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
<...>
scala> copy(dst2, src)
res1: Seq[Int] = List(1, 2, 3, 4, 50, 60)
Related
I would like to duplicate even/odd elements in a list.
def even(number: Int): Boolean = {
if(number%2 == 0) true
else false
}
I tried something weird cause i have no idea how should I do that exactly.
scala> var x = List(1, 2, 3)
x: List[Int] = List(1, 2, 3)
scala> x.map(el => if(even(el)) el::x)
res143: List[Any] = List((), List(2, 1, 2, 3), ())
This is not what I expected. I'd like to return only one list with all elements where odd/even are duplicated.
Thanks for your help.
You can use flatMap to return a list per element, either containing just the element itself if the predicate doesn't match, or a list with the element duplicated if it does:
def even(n : Int) : Boolean = n%2 == 0
val l = List(1,2,3)
l.flatMap(n => if(even(n)) List(n,n) else List(n)) // -> List(1, 2, 2, 3)
You can filter the first collection for even numbers, and than concat with the original list:
scala> var l = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> l.filter(_ % 2 == 0) ++ l
res14: List[Int] = List(2, 1, 2, 3)
If you want the List[Int] sorted, you can apply that after the concatenation:
scala> l.filter(_ % 2 == 0) ++ l sorted
res15: List[Int] = List(1, 2, 2, 3)
This saves you the allocation of a new List[Int] for every match of even. You filter only the elements you need, creating one List[Int], and then concatenating it with the original.
Since searching for :+ doesn't yield any results (thanks google!) I couldn't find any answer to my question:
Why is:
a :+ b
resulting in
List[Any]
if both 'a' and 'b' are of type
List[Int]
try it out yourself, following won't compile (Scala 2.11.6, Idea14):
object AAA extends App {
val a: List[Int] = List[Int]()
val b: List[Int] = List[Int]()
val x: List[Int] = a :+ b
}
thx in advance
:+ appends a single element to a List. So you are appending a List[Int] to a List[Int], resulting in something like (if a and b are both set to List(1, 2)):
List(1, 2, List(1, 2))
Scala calculates the most common type between the element type (Int) and the thing you append (List[Int]), which is Any.
You probably wanted to concatenate two lists:
val x: List[Int] = a ++ b
If I have
val incomingIds : List[Int] = ....
val existingIds : List[Int] = //this makes db calls and find existing records (only interested in returning ids)
Now next I want to compare incomingIds with existingIds in a following way
say I have
val incomingIds : List[Int] = List(2,3,4,5)
val existingIds : List[Int] = List(1,2,3,6)
What above sample suggests is that my API should be able to find ids that are subject for deletion (ones that exist in incomingIds but not in existingIds). In this sample existingIds have 1,4,5 but they aren't there in incomingIds means 1,4,5 should go into
val idsForDeletion :List[Int]
and there will be another list call it
val idsForInsertion :List[Int].
so 6 should go into idsForInsertion list.
Is there a simple way to partition lists such a way?
You can filter the items from each list that aren't included in the other one using scala's filterNot function:
val idsForDeletion = existingIds.filterNot(incomingIds.toSet)
val idsForInsertion = incomingIds.filterNot(existingIds.toSet)
you can use diff
def diff(that: collection.Seq[A]): List[A]
It Computes the multiset difference between this list and another sequence.
scala> val incomingIds : List[Int] = List(2,3,4,5)
incomingIds: List[Int] = List(2, 3, 4, 5)
scala> val existingIds : List[Int] = List(1,2,3,6)
existingIds: List[Int] = List(1, 2, 3, 6)
scala> (incomingIds diff existingIds).toSet
res1: scala.collection.immutable.Set[Int] = Set(4, 5)
scala> (existingIds diff incomingIds).toSet
res2: scala.collection.immutable.Set[Int] = Set(1, 6)
I have a List
val family=List("1","2","11","12","21","22","31","33","41","44","51","55")
i want to take its first n elements but the problem is that parents size is not fixed.
val familliar=List("1","2","11") //n=3
You can use take
scala> val list = List(1,2,3,4,5,6,7,8,9)
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> list.take(3)
res0: List[Int] = List(1, 2, 3)
List(1,2,3).take(100) //List(1,2,3)
The signature of take will compare the argument with index, so the incremental index will never more than argument
The signature of take
override def take(n: Int): List[A] = {
val b = new ListBuffer[A]
var i = 0
var these = this
while (!these.isEmpty && i < n) {
i += 1
b += these.head
these = these.tail
}
if (these.isEmpty) this
else b.toList
}
Use take:
val familliar = family.take(3)
I am looking for a nice way to remove first N elements which are equal from the ordered list, e.g.
List(1,1,1,2,3,3)
should return
removeSame(list) -> (1,1,1)
Is there a nice way to do it, rather than remove the head of the list and then use takeWhile on the remainder, and finally using dropwhile? I can think of simple non-functional solution but I was wondering whether any functional one also exists
The functional way to avoid the duplication of takeWhile and dropWhile to get a prefix and remainder is using span, i.e.
scala> val list = List(1,1,1,2,3,3)
list: List[Int] = List(1, 1, 1, 2, 3, 3)
scala> val (prefix, rest) = list span (_ == list.head)
prefix: List[Int] = List(1, 1, 1)
rest: List[Int] = List(2, 3, 3)
Is this what you look for?
scala> val l = List(1,1,1,2,3,3)
l: List[Int] = List(1, 1, 1, 2, 3, 3)
scala> l.takeWhile(_ == l.head)
res6: List[Int] = List(1, 1, 1)
scala> val l = List()
l: List[Nothing] = List()
scala> l.takeWhile(_ == l.head)
res7: List[Nothing] = List()
Not the best way of doing it, but this also works:
def removeSame(l: List) = if( !l.isEmpty) l.groupBy( x => x)(l.head) else List()