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)
WestCoastProjects
  • 58,982
  • 91
  • 316
  • 560
  • `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. – Mysterious Dan Sep 26 '14 at 02: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. – som-snytt Sep 29 '14 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 :( – Mysterious Dan Sep 30 '14 at 19:27

3 Answers3

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.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
misberner
  • 3,488
  • 20
  • 17
  • 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. – WestCoastProjects Sep 26 '14 at 01:33
  • @javadba - Not the closest/best you _can_ get, but that's what's there out of the box. – Rex Kerr Sep 26 '14 at 02:43
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 })
}
Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • What is the #L - I am not familiar with that construct – WestCoastProjects Sep 26 '14 at 06:17
  • 1
    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](http://stackoverflow.com/questions/8736164/what-are-type-lambdas-in-scala-and-what-are-their-benefits). – Ende Neu Sep 26 '14 at 06:23
  • @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.. – WestCoastProjects Sep 26 '14 at 06:27
  • Upvoted this answer since it appears to address the question. Can't say I understood any tiny morsel of the lambda aspect. – WestCoastProjects Sep 26 '14 at 06:29
  • Hah, yeah, this is pretty rough. I've just been playing with lambda types since I learned about them a few weeks ago - see http://stackoverflow.com/a/25801125/402884 and http://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. – Chris Martin Sep 26 '14 at 06:52
  • @javadba it's pretty harsh syntax, consider it as currying for the type system, I actually never used it (or had the need to) and if you don't feel the need to, skip it, it was nice to see in this answer and nicely used, but it's not a fundamental feature in my opinion. – Ende Neu Sep 26 '14 at 11:36
  • @EndeNeu Thanks. Whether I feel the "need" or not it is well beyond me presently - so the choice to use or not is actually not mine to make - at least without a vulcan mind meld with a more typesystem savvy individual. – WestCoastProjects Sep 26 '14 at 13:30
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)
som-snytt
  • 39,429
  • 2
  • 47
  • 129