cheseaux
cheseaux

Reputation: 5315

Append auto-incrementing suffix to duplicated elements of a List

Given the following list :

val l = List("A", "A", "C", "C", "B", "C")

How can I add an auto-incrementing suffix to every elements so that I end up with a list containing no more duplicates, like the following (the ordering doesn't matter) :

List("A0", "A1", "C0", "C1", "C2", "B0")

Upvotes: 0

Views: 480

Answers (3)

erip
erip

Reputation: 16935

Perhaps not the most readable solution, but...

def appendCount(l: List[String]): List[String] = {
  // Since we're doing zero-based counting, we need to use `getOrElse(e, -1) + 1`
  // to indicate a first-time element count as 0. 
  val counts = 
    l.foldLeft(Map[String, Int]())((acc, e) => 
      acc + (e -> (acc.getOrElse(e, -1) + 1))
    )

  val (appendedList, _) = 
    l.foldRight(List[String](), counts){ case (e, (li, m)) =>
      // Prepend the element with its count to the accumulated list.
      // Decrement that element's count within the map of element counts
      (s"$e${m(e)}" :: li, m + (e -> (m(e) - 1)))
    }
  appendedList
}

The idea here is that you create a count of each element in the list. You then iterate from the back of the list of original values and append the count to the value while decrementing the count map.

You need to define a helper here because foldRight will require both the new List[String] and the counts as an accumulator (and, as such, will return both). You'll just ignore the counts at the end (they'll all be -1 anyway).

I'd say your way is probably more clear. You'll need to benchmark to see which is faster if that's a concern.

Ideone.

Upvotes: 0

cheseaux
cheseaux

Reputation: 5315

I found it out by myself just after having written this question

val l = List("A", "A", "C", "C", "B", "C")
l.groupBy(identity) // Map(A->List(A,A),C->List(C,C,C),B->List(B))
  .values.flatMap(_.zipWithIndex) // List((A,0),(A,1),(C,0),(C,1),(C,2),(B,0))
  .map{ case (str, i) => s"$str$i"}

If there is a better solution (using foldLeft maybe) please let me know

Upvotes: 4

C4stor
C4stor

Reputation: 8026

In a single pass straightforward way :

def transformList(list : List[String]) : List[String] = {
  val buf: mutable.Map[String, Int] = mutable.Map.empty
  list.map {
    x => {
      val i = buf.getOrElseUpdate(x, 0)
      val result = s"${x.toString}$i"
      buf.put(x, i + 1)
      result
    }
  }
}

transformList( List("A", "A", "C", "C", "B", "C"))

Upvotes: 1

Related Questions