9
\$\begingroup\$

Task description: Write a function, which counts the letters, spaces, number and special characters in string.

My implementation:

fun getPartsCount(str: String): Array<Int> {
    val nums = "[0-9]".toRegex().findAll(str)
    val letters = "[a-zA-Z]".toRegex().findAll(str)
    val spaces = " ".toRegex().findAll(str)

    var others = str.count() - nums.count() - letters.count() - spaces.count()

    return arrayOf(nums.count(), letters.count(), spaces.count(), others)
}

fun main() {
    print("Please enter a string > ")
    val userInput = readLine()

    if (!userInput.isNullOrEmpty()) {
        val results = getPartsCount(userInput)
        println("Numbers: ${results[0]}, Letters: ${results[1]}, " +
                "Spaces: ${results[2]}, Others: ${results[3]}")

    } else {
        println("No input provided.")
    }
}

I solved the task using Regular Expressions, but is there a better way in Kotlin for accomplishing the task?

What's your opinion about returning the results in an array? What would you do different and why?

\$\endgroup\$

2 Answers 2

13
\$\begingroup\$

Returning an array

What's your opinion about returning the results in an array?

Why would you do that? Because it's an easy way to return the result? Because you don't know how else to do it?

How are users expected to know which index is 0, 1, 2, and 3 respectively?

It would be much better to return a class.

data class PartsCountResult(
    val numbers: Int,
    val letters: Int,
    val spaces: Int,
    val others: Int,
)

Creating a class in Kotlin is really easy. Do it when you need it. You don't even have to put it in a separate file!


Using Regex

I solved the task using Regular Expressions, but is there a better way in Kotlin for accomplishing the task?

As each character is only in one group and are easily distinguishable, you can loop over the string once and count each char.

var letters = 0
var numbers = 0
var spaces = 0
var others = 0
for (c in str) {
    when (c) {
        in 'a'..'z', in 'A'..'Z' -> letters++
        in '0'..'9' -> numbers++
        ' ' -> spaces++
        else -> others++
    }
}

Alternative solution

val letters = str.count { it in 'A'..'Z' || it in 'a'..'z' }
val digits = str.count { it in '0'..'9' }
val spaces = str.count { it == ' ' }
val others = str.length - letters - digits - spaces
\$\endgroup\$
2
  • \$\begingroup\$ At the very least, return a map instead. \$\endgroup\$
    – Nick
    Commented May 22, 2023 at 23:07
  • 2
    \$\begingroup\$ @Nick If you are suggesting a Map<String, Int> then I would recommend against that, as again you don't have any promise which keys will be available in the map. Making a class for that is much more type safe and will catch many many more issues in compile time instead of runtime. \$\endgroup\$ Commented May 23, 2023 at 16:47
7
\$\begingroup\$

Two more ways in addition to Simon's excellent answer:

The Char class has convenient methods like isDigit(), isLetter(), etc.:

val digits = str.count { it.isDigit() }
val letters = str.count { it.isLetter() }
val whitespaces = str.count { it.isWhitespace() }
val others = str.count() - digits - letters - whitespaces

Packed into one loop using list destructuring to assign the result in one go to the four variables:

val (digits, letters, spaces, others) = str
  .fold(mutableListOf(0, 0, 0, 0)) { acc, c ->
    when (true) {
      c.isDigit()      -> acc[0]++
      c.isLetter()     -> acc[1]++
      c.isWhitespace() -> acc[2]++
      else             -> acc[3]++
    }
    acc
  }
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Good suggestions, but do note that at least isLetter and isWhitespace matches way more than the original post did. Possibly also isDigit (I think there's some unicode characters in other numeral systems that will match) \$\endgroup\$ Commented May 22, 2023 at 17:54
  • 1
    \$\begingroup\$ @Simon That is true. These functions return every possible value based on the various Unicode character categories (see for example kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/is-digit.html –> ‚Nd‘ and then jump to compart.com/en/unicode/category/Nd). There are 650 entries! However, these are actually all digits one could argue. And that there might be an initial, incorrect assumption in the author's question by reducing the term 'number of digits' to [0-9]... \$\endgroup\$
    – lukas.j
    Commented May 22, 2023 at 19:48
  • \$\begingroup\$ Absolutely, I have to agree with that! \$\endgroup\$ Commented May 22, 2023 at 19:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.