0

I am still learning the basics of Scala, therefore I am asking for your understanding. Is it any possible way to use fold method to print only names beginning with "A"

Object Scala {
  val names: List[String] = List("Adam", "Mick", "Ann");
  def main(args: Array[String]) {
  println(names.foldLeft("my list of items starting with A: ")(_+_));  
    }
  }
}
  • 3
    Well you can `filter` before the fold. Or do the predicate inside the fold, like `case (acc, elem) => if (elem.startsWith("A")) acc + elem else acc }` – Luis Miguel Mejía Suárez Jun 07 '20 at 15:04
  • 1
    Building on what @LuisMiguelMejíaSuárez said, you can do something like `names.filter(_.startsWith("A")).mkString(", ")` instead of a fold – user Jun 07 '20 at 15:12

2 Answers2

3

Have a look at the signature of foldLeft

def foldLeft[B](z: B)(op: (B, A) => B): B

where

  • z is the initial value
  • op is a function taking two arguments, namely accumulated result so far B, and the next element to be processed A
  • returns the accumulated result B

Now consider this concrete implementation

val names: List[String] = List("Adam", "Mick", "Ann")
val predicate: String => Boolean = str => str.startsWith("A")

names.foldLeft(List.empty[String]) { (accumulated: List[String], next: String) =>
  if (predicate(next)) accumulated.prepended(next) else accumulated
}

here

z = List.empty[String]
op = (accumulated: List[String], next: String) => if (predicate(next)) accumulated.prepended(next) else accumulated

Usually we would write this inlined and rely on type inference so we do not have two write out full types all the time, so it becomes

names.foldLeft(List.empty[String]) { (acc, next) =>
  if (next.startsWith("A")) next :: acc else acc
}
// val res1: List[String] = List(Ann, Adam)

On of the key ideas when working with List is to always prepend an element instead of append

names.foldLeft(List.empty[String]) { (accumulated: List[String], next: String) =>
  if (predicate(next)) accumulated.appended(next) else accumulated
}

because prepending is much more efficient. However note how this makes the accumulated result in reverse order, so

List(Ann, Adam)

instead of perhaps required

List(Adam, Ann)

so often-times we perform one last traversal by calling reverse like so

names.foldLeft(List.empty[String]) { (acc, next) =>
  if (next.startsWith("A")) next :: acc else acc
}.reverse
// val res1: List[String] = List(Adam, Ann)
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
2

The answer from @Mario Galic is a good one and should be accepted. (It's the polite thing to do).

Here's a slightly different way to filter for starts-with-A strings.

val names: List[String] = List("Adam", "Mick", "Ann")

println(names.foldLeft("my list of items starting with A: "){
  case (acc, s"A$nme") => acc + s"A$nme "
  case (acc, _ ) => acc
})

//output: "my list of items starting with A: Adam Ann"
jwvh
  • 50,871
  • 7
  • 38
  • 64