canadadry
canadadry

Reputation: 8443

Concatenate a List of Char into a String via a fold

For a list of string, I do this:

val l = List("a", "r", "e")                    
l.reduceLeft((x, z) => x + z) 

I don't know how to do it for a list of Chars. The following is a compile error:

val chs = List('a', 'r', 'e')
chs.reduceLeft[String]( (x,y) => String.valueOf(x) + String.valueOf(y))

Upvotes: 3

Views: 3935

Answers (4)

JochenVdB
JochenVdB

Reputation: 31

To to show the workings of foldLeft and foldRight (and map, along the way) with a little bit af a "real" operation applied, let's use toChar (of Int):

val iA: Int = 65
val cA: Char = iA.toChar //====> A
val cB: Char = 66.toChar //====> B
cA + cB
//====> 131            (Int), since char has no concatenation +, obviously
"" + cA + cB
//====> AB             now forced to concatenation + of String

val li: List[Int] = List(65, 66, 67)
li map (i => i.toChar)   //====> List(A, B, C)

The parameter of foldLeft and foldRight is the "zero element".

I make it explicitly visible here by using "0", you want to use "" for a decent concatenation.

The zero element should generally not be part of the result, but is needed to calculate that result.

In The following code:

i: Int because of li: List[Int]

acc: String because of "0" (accumulator)

+ is String concatenation

li.foldLeft("0")((acc, i) => acc + i.toChar)
//====> 0ABC     0 --> 0A --> 0AB --> 0ABC
li.foldLeft("0")((acc, i) => i.toChar + acc)
//====> CBA0     0 --> A0 --> BA0 --> CBA0

li.foldRight("0")((i, acc) => acc + i.toChar)
//====> 0CBA     0 --> 0C --> 0CB --> 0CBA
li.foldRight("0")((i, acc) => i.toChar + acc)
//====> ABC0     0 --> C0 --> BC0 --> ABC0

Upvotes: 0

The.Anti.9
The.Anti.9

Reputation: 44648

If you want to really learn folds, you should do something a bit more applicable. While you can of course do this to a string, there are much better ways to create a string from a list of characters. Folds are very powerful and creating a string doesn't quite do it justice

Let's say, for example, you have

case class Employee(fname:String, lname:String, age:Int)

Let's say you also have a HashMap[String, List[Employee]] that organizes them by position. So you have Joe Shmoe who's a 25 year old Software Engineer, and Larry Larison who's a 37 year old accountant, etc. You can easily use a fold to organize this data into flat structures very simply. If you want to take it and create just a List of the names of your employees you can combine it with a flatMap to very simply return a List[String]

val employees = Map[String, List[Employee]]("Software Engineer" -> List(new Employee("Joe", "Shmoe", 25), new Employee("Larry", "Larrison", 37)), "Accountant" -> List(new Employee("Harry", "Harrison", 55))).flatMap(_._2).foldLeft[List[String]](Nil)((l,e) => e.fname + " " + e.lname :: l)

employees.flatMap(_._2).foldLeft[List[String]](Nil)(
  (li,emp) => 
    s"${emp.fname} ${emp.lname}" :: li
)

The flatMap function gives you a flat list of all Employee objects. It's passed a Tuple2 of String and List[Employee]. The _._2 returns the 2nd item of the Tuple2 which is the list of employees which flatMap joins together with the others.

From there, you can use foldLeft on a list of Employee objects to create a list of their names. Nil is an empty List (and will have List[String] inferred), that's your starting object.

foldLeft takes a predicate that should use a tuple as it's parameter, the first item of which will be the list formed so far, and the second item of which will be the next item in the list you are iterating over. on the first pass, you'll have an empty list and Joe Shmoe.

In the predicate, you create the string of the Employees first and last name and prepend that string to the accumulator, li.

This returns

List[String] = List(Harry Harrison, Larry Larrison, Joe Shmoe)

Folds are a very useful tool. I've found this page to be very helpful in figuring them out: http://oldfashionedsoftware.com/2009/07/30/lots-and-lots-of-foldleft-examples/

Upvotes: 1

Randall Schulz
Randall Schulz

Reputation: 26486

If you just want to accomplish the result:

scala> List('a', 'r', 'e').mkString
res0: String = are

Upvotes: 4

Noah
Noah

Reputation: 13959

Here's the type signature for reduceLeft:

def reduceLeft[B >: A](f: (B, A) => B): B 

It requires that what you're reducing to be a subtype of the type that you're reducing from so in you're case Char is A and String is B which is not a subtype of Char.

You can do a foldLeft which will reduce your list and doesn't require the output to be a subtype of the input:

chs.foldLeft("")((x,y) => x + String.valueOf(y))

Upvotes: 6

Related Questions