Reputation: 5933
Let's say you have:
List(('a', 1), ('b', 1), ('c', 1), ('b', 1))
and you want to replace the first ('b', 1)
with ('b', 2)
, and you don't want it to (a) waste time evaluating past the first match and (b) update any further matching tuples.
Is there a relatively concise way of doing this in Scala (i.e., without taking the list apart and re-concatenating it). Something like an imaginary function mapFirst
that returns the list with the first matching value incremented:
testList.mapFirst { case ('b', num) => ('b', num + 1) }
Upvotes: 2
Views: 528
Reputation: 2404
Using span
to find first element only. It shouldn't throw an exception even when case
is not satified. Need less to say, you can specify as many cases as you like.
implicit class MyRichieList[A](val l: List[A]) {
def mapFirst(pf : PartialFunction[A, A]) =
l.span(!pf.isDefinedAt(_)) match {
case (x, Nil) => x
case (x, y :: ys) => (x :+ pf(y)) ++ ys
}
}
val testList = List(('a', 1), ('b', 1), ('c', 1), ('b', 1))
testList.mapFirst {
case ('b', n) => ('b', n + 1)
case ('a', 9) => ('z', 9)
}
// result --> List((a,1), (b,2), (c,1), (b,1))
Upvotes: 1
Reputation: 5768
You don't have to take the whole List apart i guess. (Only until the element is found)
def replaceFirst[A](a : List[A], repl : A, replwith : A) : List[A] = a match {
case Nil => Nil
case head :: tail => if(head == repl) replwith :: tail else head :: replaceFirst(tail, repl, replwith)
}
The call for example:
replaceFirst(List(('a', 1), ('b', 1), ('c', 1), ('b', 1)), ('b', 1), ('b', 2))
Result:
List((a,1), (b,2), (c,1), (b,1))
A way with a partial function and implicits (which looks more like your mapFirst):
implicit class MyRichList[A](val list: List[A]) {
def mapFirst(func: PartialFunction[A, A]) = {
def mapFirst2[A](a: List[A], func: PartialFunction[A, A]): List[A] = a match {
case Nil => Nil
case head :: tail => if (func.isDefinedAt(head)) func.apply(head) :: tail else head :: mapFirst2(tail, func)
}
mapFirst2(list, func)
}
}
And use it like this:
List(('a', 1), ('b', 1), ('c', 1), ('b', 1)).mapFirst {case ('b', num) => ('b', num + 1)}
Upvotes: 3
Reputation: 12563
You can emulate such function relatively easily. The quickest (implementation-wise, not necessarily performance-wise) I could think of was something like this:
def replaceFirst[A](a:List[A], condition: (A)=>Boolean, transform:(A)=>(A)) = {
val cutoff =a.indexWhere(condition)
val (h,t) = a.splitAt(cutoff)
h ++ (transform(t.head) :: t.tail)
}
scala> replaceFirst(List(1,2,3,4,5),{x:Int => x%2==0}, { x:Int=> x*2 })
res4: List[Int] = List(1, 4, 3, 4, 5)
scala> replaceFirst(List(('a',1),('b',2),('c',3),('b',4)), {m:(Char,Int) => m._1=='b'},{m:(Char,Int) => (m._1,m._2*2)})
res6: List[(Char, Int)] = List((a,1), (b,4), (c,3), (b,4))
Upvotes: 2