Stevey
Stevey

Reputation: 3281

Is there a shorter replacement for Kotlin's deprecated String.capitalize() function?

Kotlin deprecated the capitalize function on String class, and their suggested replacement is obnoxiously long. This is an example of a situation where they made the right call on deprecating it, but the wrong call on the user experience.

For example, this code:

val x = listOf("foo", "bar", "baz").map { it.capitalize() }

is "cleaned up" by the IDE to become:

val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar {
                    if (it.isLowerCase()) it.titlecase(
                        Locale.getDefault()
                    ) else it.toString()
                } }

This is preeeeetty ugly. What can we do about it?

Upvotes: 107

Views: 43126

Answers (9)

Roki
Roki

Reputation: 1

"abcd".replaceFirstChar { it.uppercase() } -> "Abcd"

Much simple in kotlin comparing java.

Upvotes: 0

Joffrey
Joffrey

Reputation: 37799

The suggested replacement is ugly because it needs to be equivalent to what capitalize() used to do:

  1. dependent on the default locale
  2. NOT converting an uppercase first char into titlecase (e.g. capitalize does NOT transform a leading 'DŽ' character into its 'Dž' titlecase version)

If you didn't care too much about this behaviour, you can use a simpler expression using an invariant locale and unconditionally titlecasing the first character even if uppercase:

val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar(Char::titlecase) }

This means that if the first character is uppercase like 'DŽ', it will be transformed into the titlecase variant 'Dž' anyway, while the original code wouldn't touch it. This might actually be desirable.

One of the reasons capitalize() has been deprecated is because the behaviour of the method was unclear. For instance:

  • behaviour #2 is pretty weird
  • not capitalizing words in a sentence might be unexpected (C# would titlecase every space-separated word)
  • not lowercasing other characters of the words might be unexpected as well

If you want to keep the exact current behaviour on purpose, but make it more convenient to use, you can always roll your own extension function with a name that suits you ("capitalize(d)" might not give enough info to the unaware reader):

fun String.titlecaseFirstCharIfItIsLowercase() = replaceFirstChar { 
    if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() 
}

Or for the version with invariant locale that titlecases the uppercase chars:

fun String.titlecaseFirstChar() = replaceFirstChar(Char::titlecase)

Upvotes: 94

I found a method trying to capitalize a string that came from the API and it apparently worked, found it in the Kotlin docs:

println("kotlin".replaceFirstChar { it.uppercase() }) // Kotlin

and use it like this in my code:

 binding.textDescriptions.text = "${it.Year} - ${it.Type.replaceFirstChar {it.uppercase()}}"

Upvotes: 5

Vinod Kamble
Vinod Kamble

Reputation: 271

You can call the replaceFirstChar function on the original string and pass the transform function as input. The transform function takes the first character and converts it to an uppercase character using the uppercase() function.

val list = listOf("foo", "bar", "baz") .map {
     it.replaceFirstChar { firstChar ->
          firstChar.uppercase()
     }
 }
println("List - > $list")

Output

List - > [Foo, Bar, Baz]

Upvotes: 6

Aminul Haque Aome
Aminul Haque Aome

Reputation: 2609

You can use this extension function to capitalize first characture of String

fun String.capitalize(): String {
        return this.replaceFirstChar {
            if (it.isLowerCase()) it.titlecase(Locale.getDefault())
            else it.toString()
        }
}

And call this method like

"abcd".capitalize()

Upvotes: 0

TarekB
TarekB

Reputation: 777

val fruits = listOf("baNana", "avocAdo", "apPle", "kiwifRuit")
fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.lowercase().replaceFirstChar(Char::uppercase) }
    .forEach { println(it) }

Output:

Apple
Avocado

Upvotes: 8

Ahmet B.
Ahmet B.

Reputation: 1684

If you are not sure (maybe you receive Strings from an API) if the first letter is upper or lower case , you can use the below method;

var title = "myTitle"
title.replaceFirstChar {
        if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else 
        it.toString()
    }

New title will be "MyTitle"

Upvotes: 0

Adam Millerchip
Adam Millerchip

Reputation: 23139

How about this?

fun main() {
    val x = listOf("foo", "bar", "baz").map { it[0].uppercase() + it.drop(1) }
    println(x)
}

Output:

[Foo, Bar, Baz]

Upvotes: 2

Stevey
Stevey

Reputation: 3281

A neat solution is to define a new extension function on String, which hides the gory details with a cleaner name:

/**
 * Replacement for Kotlin's deprecated `capitalize()` function.
 */
fun String.capitalized(): String {
    return this.replaceFirstChar { 
        if (it.isLowerCase())
            it.titlecase(Locale.getDefault())
        else it.toString() 
    }
}

Now your old code can look like this:

val x = listOf("foo", "bar", "baz").map { it.capitalized() }

You'll need to define the extension function at the top level in some package that you can import easily. For example, if you have a kotlin file called my.package.KotlinUtils (KotlinUtils.kt), and you put the definition inside it like so:

package my.package

fun String.capitalized(): String {...}

Then you can import it in your other packages with:

import my.package.capitalized

Upvotes: 48

Related Questions