1

I have two classes A and B. Both of them have the same property: id and many other different properties.

How can I subtract Seq[A] from Seq[B] by matching the id's?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
UnguruBulan
  • 890
  • 4
  • 12
  • 24

4 Answers4

4

This should work as long as the id field of both classes have the same type.

val as: Seq[A] = ???
val bs: Seq[B] = ???

val asSet = as.iterator.map(a => a.id).toSet
val substracted: Seq[B] = bs.filterNot(b => asSet(b.id))
4

Another feasible solution:

val seqSub = seqB.filterNot(x => seqA.exists(_.id == x.id))
det0
  • 281
  • 2
  • 5
1

Couldn't find an answer matching my definition of subtract, where duplicate elements aren't filtered, (e.g. Seq(1,2,2) subtract Seq(2) = Seq(1,2), det0's definition gives Seq(1) so posting it here.

    trait IntId {
      def id: Int
    }
    
    case class A(id: Int) extends IntId
    
    case class B(id: Int) extends IntId
    
    val seqB = Seq(B(1),B(4),B(7),B(7),B(7))
    
    val seqA = Seq(A(7))

    // BSubtractA =  Seq(B(1),B(4),B(7),B(7)), only remove one instance of id 7
    val BSubtractA = seqA.foldLeft(seqB){
      case (seqBAccumulated, a) =>
        val indexOfA = seqBAccumulated.map(_.id).indexOf(a.id)
        if(indexOfA >= 0) {
          seqBAccumulated.take(indexOfA) ++ seqBAccumulated.drop(indexOfA + 1)
        }
        else {
          seqBAccumulated
        }
    }

Yes, there are shortcomings to this solution. For example, if seqA is larger than seqB , then it runs into null pointers (+ I haven't refactored it into a def). Also the performance could be improved to iterate fewer times over the input, however, this satisfied my use case.

ALee
  • 81
  • 1
  • 5
  • Instead of `acc.take(x) ++ acc.drop(x+1)` you could just `acc.patch(x, Seq(), 1)`. – jwvh Mar 17 '21 at 16:46
  • @jwvh Agreed. I copied the first answer from here: https://stackoverflow.com/questions/18847249/how-to-remove-an-item-from-a-list-in-scala-having-only-its-index But I think you're right `patch` is also valid – ALee Mar 18 '21 at 09:21
0

That will be far more clean -

val seqSub = seqB.filterNot(x => seqA.contains(x))
Maor Aharon
  • 312
  • 3
  • 14