Update multiple values in a sequence

空扰寡人 提交于 2021-02-08 05:47:44

问题


To get a sequence with one value updated, one can use

seq.updated(index, value)

I want to set a new value for a range of elements. Is there a library function for that? I currently use the following function:

def updatedSlice[A](seq: List[A], ind: Iterable[Int], value: A): List[A] = 
    if (ind.isEmpty) seq
    else updatedSlice(seq.updated(ind.head, value), ind.tail, value)

Besides the need of writing function, this seems to be inefficient, and also works only for lists, rather than arbitrary subclasses of Seq and Strings. So,

  • is there a method that performs it?
  • how can I parametrize the function to take (and return) some subclass of Seq[A]?

回答1:


No one at a computer has said:

scala> (1 to 10).toSeq patch (3, (1 to 5), 3)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 1, 2, 3, 4, 5, 7, 8, 9, 10)

Save your green checks for @Marth.

Note they're still working on it.

https://issues.scala-lang.org/browse/SI-8474

Which says something about less-frequently-used API.

Update: I glanced at the question a second time and saw that I misread it, oh well:

scala> implicit class x[A](as: Seq[A]) {
     | def updatedAt(is: collection.Traversable[Int], a: A) = {
     | (as /: is) { case (xx, i) => xx updated (i, a) } } }
defined class x

scala> (1 to 10) updatedAt (Seq(3,6,9), 0)
res9: Seq[Int] = Vector(1, 2, 3, 0, 5, 6, 0, 8, 9, 0)

Just a relaxing round of golf.

Update: s/relaxing/annoying

Looks like it needs more type params, but I don't have a time slice for it.

scala> implicit class slicer[A, B[_] <: Seq[_]](as: B[A]) {
     | def updatedAt[That<:B[_]](is: Traversable[Int], a: A)(implicit cbf: CanBuildFrom[B[A], A, That]) =
     | (as /: is) { case (x,i) => x updated[A,That] (i,a) }}
<console>:15: error: type arguments [A,That] conform to the bounds of none of the overloaded alternatives of
 value updated: [B >: _$1, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Seq[_$1],B,That])That <and> [B >: A, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Repr,B,That])That
       (as /: is) { case (x,i) => x updated[A,That] (i,a) }}
                                    ^

Who even knew updated was overloaded?

My new favorite Odersky quote:

I played with it until it got too tedious.




回答2:


To my knowledge there's no combinator that directly provides this functionality.

For the Seq part, well, it works only for List because you're taking a List as a parameter. Take a Seq, return a Seq and you already have one less problem.

Moreover, your implementation throws an IndexOutOfBounds exception if ind contains an index greater or equal to the seq length.

Here's an alternative implementation (which uses Set for a O(1) contains)

def updatedAtIndexes[A](seq: Seq[A], ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
  case (el, i) if ind.contains(i) => value
  case (el, _) => el
}

Example

updatedAtIndexes(List(1, 2, 3, 4, 5), Set(0, 2), 42) // List(42, 2, 42, 4)

You can even make it prettier with a simple implicit class:

implicit class MyPimpedSeq[A](seq: Seq[A]) {
  def updatedAtIndexes(ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
    case (el, i) if ind.contains(i) => value
    case (el, _) => el
  }
}

Examples

List(1, 2, 3, 4).updatedAtIndexes(Set(0, 2), 42) // List(42, 2, 42, 4)
Vector(1, 2, 3).updatedAtIndexes(Set(1, 2, 3), 42) // Vector(1, 42, 42)


来源:https://stackoverflow.com/questions/25450484/update-multiple-values-in-a-sequence

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!