scala / scala-library-next

backwards-binary-compatible Scala standard library additions
Apache License 2.0
69 stars 17 forks source link

Add updatedWith (and deleted) to sequences #186

Open charpov opened 3 months ago

charpov commented 3 months ago

I recently found myself writing these extensions (with help from the Scala Users forum):

extension [A, CC[_]](seq: SeqOps[A, CC, CC[A]])
   def deleted(i: Int): CC[A]                                = seq.patch(i, Nil, 1)
   def updatedWith[B >: A](i: Int, f: A => Option[B]): CC[B] = seq.patch(i, f(seq(i)), 1)

to be used as:

List(A, X, C).deleted(1)                   // List(A, C)
List(A, X, C).updatedWith(1, _ => Some(B)) // List(A, B, C)

Someone there suggested this might make sense to be part of the Seq operations in a future version of the standard library. I was actually surprised to find them missing, especially since immutable maps do have an updatedWith method. I understand that sequences are not maps, but would there be much cost to add the methods to SeqOps with a default implementation based on patch?

charpov commented 4 weeks ago

For my own usage, I ended up with this implementation:

private val none = (_: Any) => None

extension [A, CC[_]](seq: SeqOps[A, CC, CC[A]])
   def deleted(i: Int): CC[A] = updatedWith(i)(none)

   def updatedWith[B >: A](i: Int)(f: A => Option[B]): CC[B] =
      seq.iterableFactory.from:
         if i < 0 then seq
         else
            val (left, right) = seq.view.splitAt(i)
            if right.isEmpty then seq
            else if f eq none then left ++ right.tail
            else left ++ f(seq(i)) ++ right.tail

This has the benefit over patch that seq.deleted(i) eq seq when i >= seq.length (for common Seq types).