2

Scala arrays have a slice() method to return a contiguous subset. That's useful!

scala> val arr = Array(1,2,3,4,5,6,7,8)
arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8)
scala> arr.slice(2,6)
res1: Array[Int] = Array(3, 4, 5, 6)

Now how about updating a contiguous subset? What concise options do we have - i.e. potentially better than our fallback of using System.arrarycopy ?

scala> val carr = Array(111,222,333,444,555,666,777,888)
carr: Array[Int] = Array(111, 222, 333, 444, 555, 666, 777, 888)

scala> System.arraycopy(carr,3,arr,3,5)

scala> arr
res6: Array[Int] = Array(1, 2, 3, 444, 555, 666, 777, 888)
3
  • view gets you part of the way there. arr.view(2, 6) is a mutable IndexedSeq that will let you change the underlying array. Unfortunately, what you'd need then to complete the recipe is a "bulk copy to this mutable.IndexedSeq" method, and that doesn't seem to exist. Commented Sep 26, 2014 at 2:33
  • @MyseriousDan I just noticed it's not mysterious but My Serious. Anyway, transform kind of works, in league with an iterator to hide the index. Commented Sep 29, 2014 at 22:20
  • @som-snytt wow, I hadn't noticed that either. Fixed :) I considered transform but it felt icky to have a stateful transformation function bumping an iterator :( Commented Sep 30, 2014 at 19:27

3 Answers 3

7

A type-safe alternative to your System.arraycopy would be

carr.slice(3, 8).copyToArray(arr, 3)

(EDIT Note that slice() returns a new array rather than a view of the existing array. The implementation creates a new array and fills it in with System.arraycopy())

Or you could explicitly iterate over the indices

(3 until 8).foreach { i => arr(i) = carr(i) }

Both are not really concise, and you were probably hoping for something more direct like arr[3:] = carr[3:] in Python, but that's not possible.

Sign up to request clarification or add additional context in comments.

2 Comments

re: Python. Yes, it is precisely in array manipulation that python shines most brightly. I have upvoted/accepted your answer because I have sense your suggestions are the closest/best we will get with Scala.
@javadba - Not the closest/best you can get, but that's what's there out of the box.
3

Here's the closest I've been able to get.

import scala.collection.{GenIterable, mutable}

implicit class RangeOps[A, 
   S1 : ({type L[X] = X => mutable.Seq[A]})#L](as: S1) {

  def update[S3 : ({type L[X] = X => GenIterable[A]})#L]
      (r: Range, bs: S3): Unit =
    r.zip(bs).foreach({ case (i, b) => as(i) = b })
}

I wanted to be able to write arr(a to b) = .... But sadly, since the method has to be named update, and you can't use an implicit class to overload a method, the array has to be wrapped explicitly. Which kinda defeats the purpose.

val arr = (1 to 10).toArray
RangeOps(arr)(4 to 8 by 2) = Stream from 30
// arr = [1, 2, 3, 4, 30, 6, 31, 8, 32, 10]

edit - For edification, here's a simplified version that strips out the ugly generics and only works with Arrays.

implicit class RangeOps[A](as: Array[A]) {

  def update(r: Range, bs: Iterable[A]): Unit =
    r.zip(bs).foreach({ case (i, b) => as(i) = b })
}

7 Comments

What is the #L - I am not familiar with that construct
That means for all the inner classes (eg avoid path dependent type) while the ({type L[X] = X => mutable.Seq[A]})#L syntax is called lambda type.
@EndeNeu Unsure am I whether to thank you or not. (a) Yes the link addresses the topic precisely. (b) After having read the contents of that SOF question I am zero percent nearer to comprehension. Would you like to offer a brain transplant to assist further? .. Oh what the heck I'll upvote for having provided correct info..
Upvoted this answer since it appears to address the question. Can't say I understood any tiny morsel of the lambda aspect.
Hah, yeah, this is pretty rough. I've just been playing with lambda types since I learned about them a few weeks ago - see stackoverflow.com/a/25801125/402884 and stackoverflow.com/a/25882532/402884 - I just added an edit that removes that stuff since it wasn't really what you were asking about anyway.
|
1

This solution uses fewer indexes to get wrong:

scala> val is = (1 to 10).toArray
is: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> val v = is.view.slice(2,7)
v: scala.collection.mutable.IndexedSeqView[Int,Array[Int]] = SeqViewS(...)

scala> val patch = Array(111,222,333,444,555,666,777,888)
patch: Array[Int] = Array(111, 222, 333, 444, 555, 666, 777, 888)

scala> val it = patch.iterator
it: Iterator[Int] = non-empty iterator

scala> v transform (_ => it.next)
res0: v.type = SeqViewS(...)

scala> is
res1: Array[Int] = Array(1, 2, 111, 222, 333, 444, 555, 8, 9, 10)

or

scala> implicit class `seq update from`[A](ss: collection.mutable.Seq[A]) { def updateFrom(from: Iterable[A]) = { val it = from.iterator ; ss transform (_ => it.next) }}
defined class seq$u0020update$u0020from

scala> v updateFrom (990 to 999)
res2: scala.collection.mutable.Seq[Int] = SeqViewS(...)

scala> is
res3: Array[Int] = Array(1, 2, 990, 991, 992, 993, 994, 8, 9, 10)

1 Comment

Nice thinking. I have not incorporated view's much into my toolkit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.