Apply function to one element only in list or array in Scala

For any given list or array like

val list = (1 to 3).toList
val array = (1 to 3).toArray

      

and a given function that maps from and to a collection type, for example

def f(v: Int): Int = v + 10

      

how to apply f

to the i-th element list

or array

to

list.myApply(f, ith = 2)
res: List(1,12,3)

      

and

array.myApply(f, ith = 2)
res: Array(1,12,3)

      

+3


source to share


3 answers


TL; DR

import scala.collection.SeqLike
import scala.collection.generic.CanBuildFrom

implicit class Seq_[A, Repr, 
    S : ({type L[X] = X => SeqLike[A, Repr]})#L](seq: S) {

  def myApply[B >: A, That](f: A => B, ith: Int)
      (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      




Discussion

Naive approximation:

implicit class Seq_[A](seq: Seq[A]) {
  def myApply(f: A => A, ith: Int): Seq[A] =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      

Usage example:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: Seq[Int] = List(1, 12, 3)

      




Attempted actual solution:

implicit class Seq_[A, Repr <: SeqLike[A, Repr]](seq: Repr) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      

Unfortunately, implicit doesn't work. I'm not sure why.

scala> Seq_[Int, List[Int]]((1 to 3).toList).myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> Seq_[Int, List[Int]]((1 to 3).toList).myApply(_.toString + "*", ith = 2)
res: List[Any] = List(1, 2*, 3)

      




Edit: Fixed!

implicit class Seq_[A, Repr](seq: SeqLike[A, Repr]) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      

Example:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> (1 to 3).toVector.myApply(Math.pow(2, _), ith = 3)
res: scala.collection.immutable.Vector[AnyVal] = Vector(1, 2, 8.0)

      




But I just realized that you also wanted it to work for Array

, which it isn't SeqLike

, so let me think some more ...

Ah, Predef

has an implicit conversion from Array

to ArrayOps

, which is a subtype SeqLike

, so we just need to use an estimate.

implicit class Seq_[A, Repr <% SeqLike[A, Repr]](seq: Repr) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      

Finally, we have the correct behavior:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> (1 to 3).toArray.myApply(Math.pow(2, _), ith = 3)
res: Array[AnyVal] = Array(1, 2, 8.0)

      




Edit again - samthebest informs me that view restrictions are deprecated, so using this guide we can replace it with a very ugly context related.

implicit class Seq_[A, Repr, 
    S : ({type L[X] = X => SeqLike[A, Repr]})#L](seq: S) {

  def myApply[B >: A, That](f: A => B, ith: Int)
      (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

      

+7


source


Someone just asked about the patch, so maybe this is a duplicate:

scala> val list = (1 to 3).toList
list: List[Int] = List(1, 2, 3)

scala> def f(v: Int): Int = v + 10
f: (v: Int)Int

scala> def g(is: Seq[Int], f: Int => Int, i: Int) = is.patch(i, Seq(f(is(i))), 1)
g: (is: Seq[Int], f: Int => Int, i: Int)Seq[Int]

scala> g(list, f, 1)
res1: Seq[Int] = List(1, 12, 3)

      

Summarizing smidgen:

scala> def g(is: collection.Seq[Int], f: Int => Int, i: Int, count: Int = 1) = is.patch(i, is.slice(i, i + count) map f, count)
g: (is: Seq[Int], f: Int => Int, i: Int, count: Int)Seq[Int]

scala> g(list, f, 1)
res2: Seq[Int] = List(1, 12, 3)

scala> g(list, f, 1, 2)
res3: Seq[Int] = List(1, 12, 13)

      

This was my first exit, as Chris suggests:



scala> def g(is: collection.mutable.Seq[Int], f: Int => Int, i: Int) = is(i) = f(is(i))
g: (is: scala.collection.mutable.Seq[Int], f: Int => Int, i: Int)Unit

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

scala> g(as, f, 1)

scala> as
res7: Array[Int] = Array(1, 12, 3, 4, 5, 6, 7, 8, 9, 10)

      

Since Chris said:

scala> def g(is: collection.Seq[Int], f: Int => Int, i: Int) = is.updated(i, f(is(i)))
g: (is: Seq[Int], f: Int => Int, i: Int)Seq[Int]

      

And good night, Gracie.

+3


source


It is very difficult to add additional methods to existing collections with implicits. If using external method OK, this is the solution. Almost there, but not quite. Example in Scala IDE worksheet

object SeqOps {
  def applyToith(col: Seq[Int], f: Int => Int, ith: Int): Seq[Int] = {
    val indexCol = col.zipWithIndex
    indexCol.map {
      a => if (a._2 == ith) f(a._1) else a._1
    }
  }                                       //> applyToith: (col: Seq[Int], f: Int => Int, ith: Int)Seq[Int]

  def f(i: Int) = i + 10                  //> f: (i: Int)Int
  val l = List(1, 2, 3)                   //> l  : List[Int] = List(1, 2, 3)
  applyToith(l, f _, 0)                   //> res0: Seq[Int] = List(11, 2, 3)

  val a = Array(1, 2, 3)                  //> a  : Array[Int] = Array(1, 2, 3)
  applyToith(a, f _, 1)                   //> res1: Seq[Int] = ArrayBuffer(1, 12, 3)

  val v = Vector(1, 2, 3)                 //> v  : scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
  applyToith(v, f _, 2)                   //> res2: Seq[Int] = Vector(1, 2, 13)

}

      

In the case of an array, it returns ArrayBuffer instead of Array. For all other types, Seq works correctly. I've tried many other combinations but nothing fixes this problem.

val a : Seq[Int] = Array(1, 2)
a.zipWithIndex

      

This zipWithIndex returns an ArrayBuffer, but if used val a: Array[Int]

, it zipWithIndex

returns an Array.

Causal Java set bindings in the collections I think.

0


source







All Articles