简体   繁体   中英

Copy elements from one list to another in Scala

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), eg 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)

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)

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 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 Seq s, but it works on List s, of course

The require method throws IllegalArgumentException if not fulfilled at runtime

Then you need only append the last ( yx ) 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM