fortran
fortran

Reputation: 76057

How to "update" an immutable element in an immutable set in Scala?

I have a Set of elements that have their equality defined around a key, but other fields can be different... So when I need to 'update', this is what I've tried:

object sandbox {

  case class K(val id: Int, val message: String) {
    override def equals(that: Any) = that match {
      case K(this.id, _) => true
      case _ => false
    }
    override def hashCode = this.id
    override def toString = "(" + id + "," + message + ")"
  }

  val s = Set(K(1, "a"), K(2, "b"))               //> s  : scala.collection.immutable.Set[test.sandbox.K] = Set((1,a), (2,b))
  val updatedElem = K(1, "c")                     //> updatedElem  : test.sandbox.K = (1,c)

  s + updatedElem                                 //> res0: scala.collection.immutable.Set[test.sandbox.K] = Set((1,a), (2,b))

  Set(updatedElem) | s                            //> res1: scala.collection.immutable.Set[test.sandbox.K] = Set((1,c), (2,b))
  }

Adding an element that is already there won't change the set, and removing it first and adding the updated again seems kind of sub-optimal.

The union method preserves the elements of the set on the left side, but that behaviour is not documented; so I shouldn't rely on it.

So now, is there something more obvious that I am missing? Should I rely on the actual behaviour (and write a test just in case it changes)? Or should I do the update in two steps?

Upvotes: 3

Views: 1657

Answers (1)

Nan Jiang
Nan Jiang

Reputation: 1314

From my point of view, it is actually a problem of concepts in modelling. The point is that equal objects should really be equivalent... otherwise you may want to consider some other structures. For example, why not try Map that maps id to message (or id to K(id,message))? That seems much more cleaner in concepts, and then you can use .updated(1,"c") to update it.

Upvotes: 5

Related Questions