Auto Added by WPeMatico

Idiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Today in “Idiomatic Kotlin”, we’re looking at day 4 of the Advent of Code 2020 challenges, in which we tackle a problem that feels as old as programming itself: input sanitization and validation.

Day 4. Passport processing

We need to build a passport scanner that, given a batch of input text, can count how many passports are valid. You can find the complete task description at https://adventofcode.com/2020/day/4*.

Like many challenges, we first inspect our input:

ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm

iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929

hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm

The input is a batch of travel documents in a text file, separated by blank lines. Each passport is represented as a sequence of key-colon-value pairs separated by spaces or newlines. Our challenge is finding out how many passports are valid. For part one, “valid” means that they need to have all the required fields outlined by the security personnel: byr, iyr, eyr, hgt, hcl, ecl and pid (we conveniently ignore their request to validate the cid field).

Solving Day 4, Part 1

Like many challenges, we start by reading our puzzle input as text and trim off any extraneous whitespace at the beginning and the end of the file. As per the description, passports are always separated by blank lines. A blank line is just two “returns”, or newlines, in a row, so we’ll use this to split our input string into the individual passports:

val passports = File("src/day04/input.txt")
    .readText()
    .trim()
    .split("nn", "rnrn")

(Note that depending on your operating system, the line separator in text files is different: On Windows, it is rn, on Linux and macOS, it’s n. Kotlin’s split method takes an arbitrary number of delimiters, allowing us to cover both cases directly.)

We now have a list of passport strings. However, working with lists of raw strings can quickly get confusing. Let’s use Kotlin’s expressive type system to improve the situation and encapsulate the string in a very basic Passport class.

class Passport(private val text: String) {

}

We then just map the results of our split-up input to Passport objects:

// . . .
.map { Passport(it) }

From the problem description, we remember that key-value pairs are either separated by spaces or newlines within a single passport. Therefore, to get the individual pairs, we once again split our input. The delimiters, in this case, are either a space or one of the newline sequences.

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
}

We then extract the key from each passport entry. We can do so by mapping our combined fieldsWithValues to only the substring that comes before the colon:

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
    val fieldNames = fieldsWithValues.map { it.substringBefore(":") }
    return fieldNames.containsAll(requiredFields)
}

The result of our function will be whether the fieldNames we extracted contain all required fields. The requiredFields collection can be taken directly from the problem statement and translated into a list:

private val requiredFields = listOf("byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid" /*"cid"*/)

To calculate our final number, and get our first gold star for the challenge, we need to count the passports for which our function hasAllRequiredFields returns true:

fun main() {
    println(passports.count(Passport::hasAllRequiredFields))
}

With that, we have successfully solved the first part of the challenge and can set our sights on the next star in our journey.

Find the full code for the first part of the challenge on GitHub.

Solving Day 4, Part 2

In part two of the challenge, we also need to ensure that each field on the passport contains a valid value. We are given an additional list of rules to accomplish this task, which you can again find in the problem description. Years need to fall into specific ranges, as does a person’s height depending on the unit of measurement. Colors need to come from a prespecified list or follow certain patterns, and numbers must be correctly formatted.

A refactoring excursion

Before we start building the solution for part 2, let’s briefly reflect on our code and find possible changes that will make adding this functionality easier for us. At this point in the challenge, we know that our Passport class will need access to the different field names and their associated values. The classical data structure to store such kind of associative-dictionary information is a map. Let’s refactor our code to store passport information in a map instead of a string.

Because turning an input string into a map is still a process that’s associated with the Passport, I like encapsulating such logic in a companion object “factory” function. In this case, we can aptly call it fromString:

companion object {
    fun fromString(s: String): Passport {

The implementation for fromString partially reuses the normalization logic we had previously used in the first part of this challenge and expands it to create a map directly via Kotlin’s associate function:

fun fromString(s: String): Passport {
    val fieldsAndValues = s.split(" ", "n", "rn")
    val map = fieldsAndValues.associate {
        val (key, value) = it.split(":")
        key to value
    }
    return Passport(map)
}

A Passport object now encapsulates a map of string keys and string values:

class Passport(private val map: Map<String, String>) {

Interestingly enough, this change makes the implementation of the first part of our challenge trivial. We can simply check that the key set of our map contains all required fields:

fun hasAllRequiredFields() = map.keys.containsAll(requiredFields)

Returning to solving part 2

For the second part of the challenge, we consider a passport valid if it contains all the required fields and has values that correspond to the official rules.

To ensure that all fields have valid values, we can use the all function to assert that a predicate holds true for every single key-value pair in our map:

fun hasValidValues(): Boolean =
    map.all { (key, value) ->

We can distinguish the different types of fields using a when expression. In this first step, we distinguish the different cases based on the keys in our map:

when (key) {

Each key we know gets a branch in this when statement. They all need to return a boolean value – true if the field is okay, false if the field violates the rules. The surrounding all predicate will then use those results to determine whether the passport as a whole is valid.

The byr (Birth Year), iyr (Issue Year), and eyr (Expiration Year) fields all require their value to be a 4-digit number falling into a particular range:

"byr" -> value.length == 4 && value.toIntOrNull() in 1920..2002
"iyr" -> value.length == 4 && value.toIntOrNull() in 2010..2020
"eyr" -> value.length == 4 && value.toIntOrNull() in 2020..2030

Note that our combined use of toIntOrNull together with the infix function in allows us to discard any non-numeric values, and ensure that they fall in the correct range.

We can apply a very similar rule to the pid (Passport ID) field. We ensure that the length of the value is correct and ensure that all characters belong to the set of digits:

"pid" -> value.length == 9 && value.all(Char::isDigit)

Validating ecl (eye color) just requires us to check whether the input is in a certain set of values, similar to the first part of our challenge:

"ecl" -> value in setOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth")

At this point, we have two more fields to validate: hgt (Height) and hcl (Hair Color). Both of them are a bit more tricky. Let’s look at the hgt field first.

The hgt (Height) field can contain a measurement either in centimeters or inches. Depending on the unit used, different values are allowed. Thankfully, both “cm” and “in” are two-character suffixes. This means we can again use Kotlin’s when function, grab the last two characters in the field value and differentiate the validation logic for centimeters and inches. Like our other number-validation logic, we parse the integer and check whether it belongs to a specific range. To do so, we also remove the unit suffix:

"hgt" -> when (value.takeLast(2)) {
    "cm" -> value.removeSuffix("cm").toIntOrNull() in 150..193
    "in" -> value.removeSuffix("in").toIntOrNull() in 59..76
    else -> false
}

The last field to validate is hcl (Hair Color), which expects a # followed by exactly six hexadecimal digits – digits from 0 through 9, and a through f. While Kotlin can parse base-16 numbers, we can use this case to show off the sledgehammer method for validating patterns – regular expressions. Those can be defined as Kotlin strings and converted using the toRegex function. Triple-quoted strings can help with escape characters:

"hcl" -> value matches """#[0-9a-f]{6}""".toRegex()

Our hand-crafted pattern matches exactly one hashtag, then six characters from the group of 0 through 9, a through f.

As a short aside for performance enthusiasts: toRegex is a relatively expensive function, so it may be worth moving this function call into a constant. The same also applies to the set used in the validation for ecl – currently, it is initialized on each test.

Because the whole when-block is used as an expression, we need to ensure that all possible branches are covered. In our case, that just means adding an else branch, which simply returns true – just because a passport has a field we don’t know about doesn’t mean it can’t still be valid.

else -> true

With that, we have covered every rule outlined to us by the problem statement. To get our reward, we can now just count the passports that contain all required fields and have valid values:

fun partTwo() {
    println(passports.count { it.hasAllRequiredFields() && it.hasValidValues() })
}

We end up with a resulting number, which we can exchange for the second star. We’re clear for boarding our virtual flight. Though this was probably not the last challenge that awaits us…

Find the complete solution for the second part of the challenge on GitHub.

Conclusion

For today’s challenge, we came up with an elegant solution to validate specific string information, which we extracted using utility functions offered by the Kotlin standard library. As the challenge continued, we reflected on our code, identified more fitting data structures, and changed our logic to accommodate it. Using Kotlin’s when statement, we were able to keep the validation logic concise and all in one place. We saw multiple different ways of how to validate input – working with ranges, checking set membership, or matching a particular regular expression, for example.

Many real-world applications have similar requirements for input validation. Hopefully, some of the tips and tricks you’ve seen in the context of this little challenge will also be helpful when you need to write some validation logic on your own.

To continue puzzling yourself, check out adventofcode.com, whose organizers kindly permitted us to use their problem statements for this series.

If you want to see more solutions for Advent of Code challenges in the form of videos, subscribe to our YouTube channel and hit the bell to get notified when we continue our idiomatic journey. More puzzle solutions are coming your way!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingIdiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Idiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Today in “Idiomatic Kotlin”, we’re looking at day 4 of the Advent of Code 2020 challenges, in which we tackle a problem that feels as old as programming itself: input sanitization and validation.

Day 4. Passport processing

We need to build a passport scanner that, given a batch of input text, can count how many passports are valid. You can find the complete task description at https://adventofcode.com/2020/day/4*.

Like many challenges, we first inspect our input:

ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm

iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929

hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm

The input is a batch of travel documents in a text file, separated by blank lines. Each passport is represented as a sequence of key-colon-value pairs separated by spaces or newlines. Our challenge is finding out how many passports are valid. For part one, “valid” means that they need to have all the required fields outlined by the security personnel: byr, iyr, eyr, hgt, hcl, ecl and pid (we conveniently ignore their request to validate the cid field).

Solving Day 4, Part 1

Like many challenges, we start by reading our puzzle input as text and trim off any extraneous whitespace at the beginning and the end of the file. As per the description, passports are always separated by blank lines. A blank line is just two “returns”, or newlines, in a row, so we’ll use this to split our input string into the individual passports:

val passports = File("src/day04/input.txt")
    .readText()
    .trim()
    .split("nn", "rnrn")

(Note that depending on your operating system, the line separator in text files is different: On Windows, it is rn, on Linux and macOS, it’s n. Kotlin’s split method takes an arbitrary number of delimiters, allowing us to cover both cases directly.)

We now have a list of passport strings. However, working with lists of raw strings can quickly get confusing. Let’s use Kotlin’s expressive type system to improve the situation and encapsulate the string in a very basic Passport class.

class Passport(private val text: String) {

}

We then just map the results of our split-up input to Passport objects:

// . . .
.map { Passport(it) }

From the problem description, we remember that key-value pairs are either separated by spaces or newlines within a single passport. Therefore, to get the individual pairs, we once again split our input. The delimiters, in this case, are either a space or one of the newline sequences.

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
}

We then extract the key from each passport entry. We can do so by mapping our combined fieldsWithValues to only the substring that comes before the colon:

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
    val fieldNames = fieldsWithValues.map { it.substringBefore(":") }
    return fieldNames.containsAll(requiredFields)
}

The result of our function will be whether the fieldNames we extracted contain all required fields. The requiredFields collection can be taken directly from the problem statement and translated into a list:

private val requiredFields = listOf("byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid" /*"cid"*/)

To calculate our final number, and get our first gold star for the challenge, we need to count the passports for which our function hasAllRequiredFields returns true:

fun main() {
    println(passports.count(Passport::hasAllRequiredFields))
}

With that, we have successfully solved the first part of the challenge and can set our sights on the next star in our journey.

Find the full code for the first part of the challenge on GitHub.

Solving Day 4, Part 2

In part two of the challenge, we also need to ensure that each field on the passport contains a valid value. We are given an additional list of rules to accomplish this task, which you can again find in the problem description. Years need to fall into specific ranges, as does a person’s height depending on the unit of measurement. Colors need to come from a prespecified list or follow certain patterns, and numbers must be correctly formatted.

A refactoring excursion

Before we start building the solution for part 2, let’s briefly reflect on our code and find possible changes that will make adding this functionality easier for us. At this point in the challenge, we know that our Passport class will need access to the different field names and their associated values. The classical data structure to store such kind of associative-dictionary information is a map. Let’s refactor our code to store passport information in a map instead of a string.

Because turning an input string into a map is still a process that’s associated with the Passport, I like encapsulating such logic in a companion object “factory” function. In this case, we can aptly call it fromString:

companion object {
    fun fromString(s: String): Passport {

The implementation for fromString partially reuses the normalization logic we had previously used in the first part of this challenge and expands it to create a map directly via Kotlin’s associate function:

fun fromString(s: String): Passport {
    val fieldsAndValues = s.split(" ", "n", "rn")
    val map = fieldsAndValues.associate {
        val (key, value) = it.split(":")
        key to value
    }
    return Passport(map)
}

A Passport object now encapsulates a map of string keys and string values:

class Passport(private val map: Map<String, String>) {

Interestingly enough, this change makes the implementation of the first part of our challenge trivial. We can simply check that the key set of our map contains all required fields:

fun hasAllRequiredFields() = map.keys.containsAll(requiredFields)

Returning to solving part 2

For the second part of the challenge, we consider a passport valid if it contains all the required fields and has values that correspond to the official rules.

To ensure that all fields have valid values, we can use the all function to assert that a predicate holds true for every single key-value pair in our map:

fun hasValidValues(): Boolean =
    map.all { (key, value) ->

We can distinguish the different types of fields using a when expression. In this first step, we distinguish the different cases based on the keys in our map:

when (key) {

Each key we know gets a branch in this when statement. They all need to return a boolean value – true if the field is okay, false if the field violates the rules. The surrounding all predicate will then use those results to determine whether the passport as a whole is valid.

The byr (Birth Year), iyr (Issue Year), and eyr (Expiration Year) fields all require their value to be a 4-digit number falling into a particular range:

"byr" -> value.length == 4 && value.toIntOrNull() in 1920..2002
"iyr" -> value.length == 4 && value.toIntOrNull() in 2010..2020
"eyr" -> value.length == 4 && value.toIntOrNull() in 2020..2030

Note that our combined use of toIntOrNull together with the infix function in allows us to discard any non-numeric values, and ensure that they fall in the correct range.

We can apply a very similar rule to the pid (Passport ID) field. We ensure that the length of the value is correct and ensure that all characters belong to the set of digits:

"pid" -> value.length == 9 && value.all(Char::isDigit)

Validating ecl (eye color) just requires us to check whether the input is in a certain set of values, similar to the first part of our challenge:

"ecl" -> value in setOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth")

At this point, we have two more fields to validate: hgt (Height) and hcl (Hair Color). Both of them are a bit more tricky. Let’s look at the hgt field first.

The hgt (Height) field can contain a measurement either in centimeters or inches. Depending on the unit used, different values are allowed. Thankfully, both “cm” and “in” are two-character suffixes. This means we can again use Kotlin’s when function, grab the last two characters in the field value and differentiate the validation logic for centimeters and inches. Like our other number-validation logic, we parse the integer and check whether it belongs to a specific range. To do so, we also remove the unit suffix:

"hgt" -> when (value.takeLast(2)) {
    "cm" -> value.removeSuffix("cm").toIntOrNull() in 150..193
    "in" -> value.removeSuffix("in").toIntOrNull() in 59..76
    else -> false
}

The last field to validate is hcl (Hair Color), which expects a # followed by exactly six hexadecimal digits – digits from 0 through 9, and a through f. While Kotlin can parse base-16 numbers, we can use this case to show off the sledgehammer method for validating patterns – regular expressions. Those can be defined as Kotlin strings and converted using the toRegex function. Triple-quoted strings can help with escape characters:

"hcl" -> value matches """#[0-9a-f]{6}""".toRegex()

Our hand-crafted pattern matches exactly one hashtag, then six characters from the group of 0 through 9, a through f.

As a short aside for performance enthusiasts: toRegex is a relatively expensive function, so it may be worth moving this function call into a constant. The same also applies to the set used in the validation for ecl – currently, it is initialized on each test.

Because the whole when-block is used as an expression, we need to ensure that all possible branches are covered. In our case, that just means adding an else branch, which simply returns true – just because a passport has a field we don’t know about doesn’t mean it can’t still be valid.

else -> true

With that, we have covered every rule outlined to us by the problem statement. To get our reward, we can now just count the passports that contain all required fields and have valid values:

fun partTwo() {
    println(passports.count { it.hasAllRequiredFields() && it.hasValidValues() })
}

We end up with a resulting number, which we can exchange for the second star. We’re clear for boarding our virtual flight. Though this was probably not the last challenge that awaits us…

Find the complete solution for the second part of the challenge on GitHub.

Conclusion

For today’s challenge, we came up with an elegant solution to validate specific string information, which we extracted using utility functions offered by the Kotlin standard library. As the challenge continued, we reflected on our code, identified more fitting data structures, and changed our logic to accommodate it. Using Kotlin’s when statement, we were able to keep the validation logic concise and all in one place. We saw multiple different ways of how to validate input – working with ranges, checking set membership, or matching a particular regular expression, for example.

Many real-world applications have similar requirements for input validation. Hopefully, some of the tips and tricks you’ve seen in the context of this little challenge will also be helpful when you need to write some validation logic on your own.

To continue puzzling yourself, check out adventofcode.com, whose organizers kindly permitted us to use their problem statements for this series.

If you want to see more solutions for Advent of Code challenges in the form of videos, subscribe to our YouTube channel and hit the bell to get notified when we continue our idiomatic journey. More puzzle solutions are coming your way!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingIdiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Idiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Today in “Idiomatic Kotlin”, we’re looking at day 4 of the Advent of Code 2020 challenges, in which we tackle a problem that feels as old as programming itself: input sanitization and validation.

Day 4. Passport processing

We need to build a passport scanner that, given a batch of input text, can count how many passports are valid. You can find the complete task description at https://adventofcode.com/2020/day/4*.

Like many challenges, we first inspect our input:

ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm

iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929

hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm

The input is a batch of travel documents in a text file, separated by blank lines. Each passport is represented as a sequence of key-colon-value pairs separated by spaces or newlines. Our challenge is finding out how many passports are valid. For part one, “valid” means that they need to have all the required fields outlined by the security personnel: byr, iyr, eyr, hgt, hcl, ecl and pid (we conveniently ignore their request to validate the cid field).

Solving Day 4, Part 1

Like many challenges, we start by reading our puzzle input as text and trim off any extraneous whitespace at the beginning and the end of the file. As per the description, passports are always separated by blank lines. A blank line is just two “returns”, or newlines, in a row, so we’ll use this to split our input string into the individual passports:

val passports = File("src/day04/input.txt")
    .readText()
    .trim()
    .split("nn", "rnrn")

(Note that depending on your operating system, the line separator in text files is different: On Windows, it is rn, on Linux and macOS, it’s n. Kotlin’s split method takes an arbitrary number of delimiters, allowing us to cover both cases directly.)

We now have a list of passport strings. However, working with lists of raw strings can quickly get confusing. Let’s use Kotlin’s expressive type system to improve the situation and encapsulate the string in a very basic Passport class.

class Passport(private val text: String) {

}

We then just map the results of our split-up input to Passport objects:

// . . .
.map { Passport(it) }

From the problem description, we remember that key-value pairs are either separated by spaces or newlines within a single passport. Therefore, to get the individual pairs, we once again split our input. The delimiters, in this case, are either a space or one of the newline sequences.

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
}

We then extract the key from each passport entry. We can do so by mapping our combined fieldsWithValues to only the substring that comes before the colon:

fun hasAllRequiredFields(): Boolean {
    val fieldsWithValues = text.split(" ", "n", "rn")
    val fieldNames = fieldsWithValues.map { it.substringBefore(":") }
    return fieldNames.containsAll(requiredFields)
}

The result of our function will be whether the fieldNames we extracted contain all required fields. The requiredFields collection can be taken directly from the problem statement and translated into a list:

private val requiredFields = listOf("byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid" /*"cid"*/)

To calculate our final number, and get our first gold star for the challenge, we need to count the passports for which our function hasAllRequiredFields returns true:

fun main() {
    println(passports.count(Passport::hasAllRequiredFields))
}

With that, we have successfully solved the first part of the challenge and can set our sights on the next star in our journey.

Find the full code for the first part of the challenge on GitHub.

Solving Day 4, Part 2

In part two of the challenge, we also need to ensure that each field on the passport contains a valid value. We are given an additional list of rules to accomplish this task, which you can again find in the problem description. Years need to fall into specific ranges, as does a person’s height depending on the unit of measurement. Colors need to come from a prespecified list or follow certain patterns, and numbers must be correctly formatted.

A refactoring excursion

Before we start building the solution for part 2, let’s briefly reflect on our code and find possible changes that will make adding this functionality easier for us. At this point in the challenge, we know that our Passport class will need access to the different field names and their associated values. The classical data structure to store such kind of associative-dictionary information is a map. Let’s refactor our code to store passport information in a map instead of a string.

Because turning an input string into a map is still a process that’s associated with the Passport, I like encapsulating such logic in a companion object “factory” function. In this case, we can aptly call it fromString:

companion object {
    fun fromString(s: String): Passport {

The implementation for fromString partially reuses the normalization logic we had previously used in the first part of this challenge and expands it to create a map directly via Kotlin’s associate function:

fun fromString(s: String): Passport {
    val fieldsAndValues = s.split(" ", "n", "rn")
    val map = fieldsAndValues.associate {
        val (key, value) = it.split(":")
        key to value
    }
    return Passport(map)
}

A Passport object now encapsulates a map of string keys and string values:

class Passport(private val map: Map<String, String>) {

Interestingly enough, this change makes the implementation of the first part of our challenge trivial. We can simply check that the key set of our map contains all required fields:

fun hasAllRequiredFields() = map.keys.containsAll(requiredFields)

Returning to solving part 2

For the second part of the challenge, we consider a passport valid if it contains all the required fields and has values that correspond to the official rules.

To ensure that all fields have valid values, we can use the all function to assert that a predicate holds true for every single key-value pair in our map:

fun hasValidValues(): Boolean =
    map.all { (key, value) ->

We can distinguish the different types of fields using a when expression. In this first step, we distinguish the different cases based on the keys in our map:

when (key) {

Each key we know gets a branch in this when statement. They all need to return a boolean value – true if the field is okay, false if the field violates the rules. The surrounding all predicate will then use those results to determine whether the passport as a whole is valid.

The byr (Birth Year), iyr (Issue Year), and eyr (Expiration Year) fields all require their value to be a 4-digit number falling into a particular range:

"byr" -> value.length == 4 && value.toIntOrNull() in 1920..2002
"iyr" -> value.length == 4 && value.toIntOrNull() in 2010..2020
"eyr" -> value.length == 4 && value.toIntOrNull() in 2020..2030

Note that our combined use of toIntOrNull together with the infix function in allows us to discard any non-numeric values, and ensure that they fall in the correct range.

We can apply a very similar rule to the pid (Passport ID) field. We ensure that the length of the value is correct and ensure that all characters belong to the set of digits:

"pid" -> value.length == 9 && value.all(Char::isDigit)

Validating ecl (eye color) just requires us to check whether the input is in a certain set of values, similar to the first part of our challenge:

"ecl" -> value in setOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth")

At this point, we have two more fields to validate: hgt (Height) and hcl (Hair Color). Both of them are a bit more tricky. Let’s look at the hgt field first.

The hgt (Height) field can contain a measurement either in centimeters or inches. Depending on the unit used, different values are allowed. Thankfully, both “cm” and “in” are two-character suffixes. This means we can again use Kotlin’s when function, grab the last two characters in the field value and differentiate the validation logic for centimeters and inches. Like our other number-validation logic, we parse the integer and check whether it belongs to a specific range. To do so, we also remove the unit suffix:

"hgt" -> when (value.takeLast(2)) {
    "cm" -> value.removeSuffix("cm").toIntOrNull() in 150..193
    "in" -> value.removeSuffix("in").toIntOrNull() in 59..76
    else -> false
}

The last field to validate is hcl (Hair Color), which expects a # followed by exactly six hexadecimal digits – digits from 0 through 9, and a through f. While Kotlin can parse base-16 numbers, we can use this case to show off the sledgehammer method for validating patterns – regular expressions. Those can be defined as Kotlin strings and converted using the toRegex function. Triple-quoted strings can help with escape characters:

"hcl" -> value matches """#[0-9a-f]{6}""".toRegex()

Our hand-crafted pattern matches exactly one hashtag, then six characters from the group of 0 through 9, a through f.

As a short aside for performance enthusiasts: toRegex is a relatively expensive function, so it may be worth moving this function call into a constant. The same also applies to the set used in the validation for ecl – currently, it is initialized on each test.

Because the whole when-block is used as an expression, we need to ensure that all possible branches are covered. In our case, that just means adding an else branch, which simply returns true – just because a passport has a field we don’t know about doesn’t mean it can’t still be valid.

else -> true

With that, we have covered every rule outlined to us by the problem statement. To get our reward, we can now just count the passports that contain all required fields and have valid values:

fun partTwo() {
    println(passports.count { it.hasAllRequiredFields() && it.hasValidValues() })
}

We end up with a resulting number, which we can exchange for the second star. We’re clear for boarding our virtual flight. Though this was probably not the last challenge that awaits us…

Find the complete solution for the second part of the challenge on GitHub.

Conclusion

For today’s challenge, we came up with an elegant solution to validate specific string information, which we extracted using utility functions offered by the Kotlin standard library. As the challenge continued, we reflected on our code, identified more fitting data structures, and changed our logic to accommodate it. Using Kotlin’s when statement, we were able to keep the validation logic concise and all in one place. We saw multiple different ways of how to validate input – working with ranges, checking set membership, or matching a particular regular expression, for example.

Many real-world applications have similar requirements for input validation. Hopefully, some of the tips and tricks you’ve seen in the context of this little challenge will also be helpful when you need to write some validation logic on your own.

To continue puzzling yourself, check out adventofcode.com, whose organizers kindly permitted us to use their problem statements for this series.

If you want to see more solutions for Advent of Code challenges in the form of videos, subscribe to our YouTube channel and hit the bell to get notified when we continue our idiomatic journey. More puzzle solutions are coming your way!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingIdiomatic Kotlin: Solving Advent of Code Puzzles, Passport Validation

Learn the Kotlin Basics for Free on JetBrains Academy

Today we have great news for those of you who are just starting out on your programming journey with Kotlin or who want to develop new programming skills. JetBrains Academy recently released a new Kotlin Basics track! It helps you get to grasps with all the Kotlin programming language essentials as you build fully functional applications.

As the company behind Kotlin, we’re providing individual learners with free access to the track! All you need to do is to register free at JetBrains Academy, choose the Kotlin Basics track, select a project, and start learning.

Many teams contributed to making the Kotlin Basics track, including our Kotlin team! We all are proud to offer high-quality educational materials that would be a great starting point for anyone interested in Kotlin. Kotlin Basics presents 10 real-world projects of different proficiency levels, over 120 educational topics, and more than 300 coding challenges.

With JetBrains Academy, a hands-on learning platform for programming, you’ll try a holistic approach to learning in which you study the theory and then immediately put it into practice by creating working applications. You will have all of the powerful features of JetBrains Academy to help you along the way. For instance, a personalized study plan divided into several stages of increasing complexity will help you gradually expand your knowledge in Kotlin without getting overwhelmed.

study-plan

Integration with JetBrains IDEs will augment your learning process by providing smart coding assistance, such as code analysis, refactorings, and code completion. They also offer you the opportunity to get experience with professional development tools while you learn to program.

ide

The Knowledge map shows you how all of the topics are connected at a glance. It will help you find the gaps in your knowledge and show you the topics you can learn to fill them.

With no prior experience, you’ll be able to create real applications, such as a chatbot or your own converter that handles weight, temperature units, and distance! The projects you complete while learning can also be published on GitHub to build your developer portfolio.

If you already have a grasp of Kotlin and want to build on your skills, JetBrains Academy has something for you, too. The Kotlin Developer track has just graduated from Beta! Take advantage of this subscription track to challenge yourself with more advanced projects and gain more in-depth knowledge of Kotlin.

If you are new to JetBrains Academy, you can start a free 7-day trial with the Kotlin Developer track and extend it by up to 2 months by working on your first project! To do that, complete the first stage of your project within the first 7 days and have your trial extended by 1 month. If you finish your first project within that first month, you will have one more month added to your trial – no payment information required.

Whether you’d just like to check out Kotlin or aspire to become a Kotlin developer, we hope you’ll find our Kotlin Basic and Kotlin Developer tracks useful!

Continue ReadingLearn the Kotlin Basics for Free on JetBrains Academy

Idiomatic Kotlin: Solving Advent of Code Puzzles, Day 2

Let’s continue learning how to write idiomatic Kotlin code by solving the Advent of Code tasks! Today, we’re discussing the solution for the day 2 task.

Day 2. Password philosophy

We need to confirm that passwords meet the corporate policy. Find the full task description at https://adventofcode.com/2020/day/2*.

First, we need to read the input:

1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc

Each line contains the password policy and the password. Our task is to check that the password is valid and conforms to the given policy. The policies are different in the first and the second parts of the task.

In the first part, the password policy indicates the lowest and highest number of times a given letter must appear for the password to be valid. For example, 1-3 a means that the password must contain a at least once and at most 3 times. In the example, two passwords, the first and the third ones, are valid. The first contains one a, and the third contains nine cs, both within the limits of their respective policies. The second password, cdefg, is invalid, as it contains no instances of b but needs at least 1.

In the second part, the policy describes two positions in the password, where 1 means the first character, 2 means the second character, and so on (indexing starts at 1, not 0). Exactly one of these positions must contain the given letter. Other occurrences of the letter are irrelevant. Given the same example list from above:

  1. 1-3 a: abcde is valid: position 1 contains a and position 3 does not.
  2. 1-3 b: cdefg is invalid: neither position 1 nor position 3 contains b.
  3. 2-9 c: ccccccccc is invalid: both positions 2 and position 9 contain c.

We should count the number of passwords from the given input that are valid according to the interpretations of the policies.

As usual, if you haven’t done it, please solve the task yourself first. Here’s how you can set up Kotlin for this purpose.

Solution

First, we should read and parse the input. Let’s create a data class for storing a given password together with its policy:

data class PasswordWithPolicy(
   val password: String,
   val range: IntRange,
   val letter: Char
)

Each policy specifies an integer range and a letter that we store in the IntRange and Char types, accordingly. The modifier data instructs the compiler to generate some useful methods for this class, including the constructor, equals, hashCode, and toString.

Parsing input

Let’s write a function that parses one input line into this class. Since it’s a function that creates an instance, let’s put it into a companion object in our data class. Then we can call it by the class name: PasswordWithPolicy.parse().

data class PasswordWithPolicy(...) {
   companion object {
       fun parse(line: String): PasswordWithPolicy { 
           TODO()
       }
   }
}

While developing, you can put the TODO() call that throws the NotImplementedError exception. Because it returns the Nothing type, which is a subtype of any other type, the compiler won’t complain. The function is supposed to return PasswordWithPolicy, but throwing an exception inside is totally valid from the compiler’s point of view.

There are two ways to parse the input: by using utility functions on Strings or by using regular expressions. Let’s discuss both of these ways.

The Kotlin standard library contains many useful functions on Strings, including substringAfter() and substringBefore() which perfectly solve the task in our case:

// 1-3 a: abcde
fun parse(line: String) = PasswordWithPolicy(
   password = line.substringAfter(": "),
   letter = line.substringAfter(" ").substringBefore(":").single(),
   range = line.substringBefore(" ").let {
       val (start, end) = it.split("-")
       start.toInt()..end.toInt()
   },
)

We provide the parameter names explicitly while calling the PasswordWithPolicy constructor. The input format convention requires that:

  • password be the string that goes right after the colon
  • the letter go between the whitespace and the colon, and
  • the range go before the whitespace and consist of two numbers split by “-

To build a range, we first take the substring before the whitespace and split it with the “-” delimiter. We then use only the first two parts (start and end), and build a range using the “..” operator converting both lines to Ints. We use let to convert the result of substringBefore() to the desired range.

In this code, we assume that all the input lines follow the rules. In a real-world scenario, we should check that the input is correct. substringAfter() and similar functions take a second argument default to the string itself that should be returned in the event the given delimiter is not found. toInt() and single() have toIntOrNull() and singleOrNull() counterparts returning null if the string can’t be converted to an integer number or consists of more than one character.

Let’s now implement the same logic using regular expressions. Here is a regular expression describing the input format:

(d+)-(d+) ([a-z]): ([a-z]+)

First are two numbers split by -, then a letter and a password.

Our new parse function matches the line against a regular expression and then builds PasswordWithPolicy on the result:

private val regex = Regex("""(d+)-(d+) ([a-z]): ([a-z]+)""")
fun parseUsingRegex(line: String): PasswordWithPolicy =
   regex.matchEntire(line)!!
       .destructured
       .let { (start, end, letter, password) ->
           PasswordWithPolicy(password, start.toInt()..end.toInt(), letter.single())
       }

In Kotlin, you use the Regex class to define a regular expression. Note how we put it inside a triple-quoted string, so you don’t need to escape . For regular strings, escaping is done in the conventional way, with a backslash, so if you need a backslash character in the string, you repeat it: "(\d+)-(\d+)".

We assume that our input is correct, which is why we use !! to throw an NPE if the input doesn’t correspond to the regular expression. An alternative is to use the safe access ?. here and return null as the result.

The destructured property provides components for a destructuring assignment for groups defined in the regular expression. We use its result together with let and destruct it inside the lambda expression, defining start, end, letter, and password as parameters. As before, we need to convert strings to Ints and Char.

Password validation

After we read the input, we need to validate whether the passwords comply with the given policy.

In the first part, we need to ensure that the number of occurrences of a given letter is in a range. It’s one line in Kotlin:

data class PasswordWithPolicy(
   val password: String,
   val range: IntRange,
   val letter: Char
) {
   fun validatePartOne() =
       password.count { it == letter } in range
   ...
}

We count the occurrences of letter in the password string, and check that the result is in range. in checks that the given element belongs to a range. For numbers and other comparable elements, x in range is the same as writing explicitly range.first <= x && x <= range.last.

The second part isn’t difficult. We need to check that exactly one of the positions (stored in the range) contains the given letter. We use the boolean xor operator for that, which returns true if the operands are different. That’s exactly what we need here:

fun validatePartTwo() =
   (password[range.first - 1] == letter) xor (password[range.last - 1] == letter)

We need to subtract 1 from first and last because they imply indexing from one, but string indexation starts from zero.

Added all together, this gives us our main function that finds the result for both parts:

fun main() {
   val passwords = File("src/day2/input.txt")
       .readLines()
       .map(PasswordWithPolicy::parse)
   println(passwords.count { it.validatePartOne() })
   println(passwords.count { it.validatePartTwo() })
}

Note how we pass a member reference PasswordWithPolicy::parse to the map function to convert the strings to passwords.

That’s it! We discussed how to solve this problem, and along the way, looked at string utility functions, regular expressions, operations on collections, and how let helps transform the expression nicely to the form you need.

Please let us know if you find this format useful and would like us to provide solutions for more difficult tasks!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingIdiomatic Kotlin: Solving Advent of Code Puzzles, Day 2

Solving Advent of Code Puzzles in Idiomatic Kotlin

What’s the best way to learn a language other than writing some code with it? Solving fun and short tasks like the ones from Advent of Code might be a great opportunity to practice your language skills, and you can learn a lot if you compare your solutions with how others have solved the same problem.

Lots of developers from around the world, including some from the Kotlin team, take part in the Advent of Code challenges created by Eric Wastl. Advent of Code is a series of tasks published every December, which you solve and compete with others. Many would agree that it’s the best advent calendar to celebrate Christmas and New Year!

To help the community learn idiomatic Kotlin, and motivate more developers to solve Advent of Code tasks in Kotlin in the future, we decided to prepare solutions for the tasks from Advent of Code 2020. It doesn’t matter if you solved it back in December, you’re ready to solve it now, or you just want to check the solutions – we hope you’ll find something useful in these materials. Of course, it works best if you try to solve the same task first yourself!

Below is the solution and video for the first task. If you find this format useful and want us to cover more tasks in a similar fashion, please share in the comments!

Day 1. Report Repair

We’re fixing an expense report! Find the full task description at https://adventofcode.com/2020/day/1*.

You need to find the two (and in the second part, three) entries from the list of numbers that sum to 2020 and then multiply those two (or three) numbers together.

How to solve the task

Register at https://adventofcode.com/, open the task at https://adventofcode.com/2020/day/1, write your solution in Kotlin, and check the result on the site. You can either write Kotlin code online or using an IDE:

Finally, compare your solution with the solution below.

We marked the src folder as a source set to put the code directly there. We copied input files, like src/day1/input.txt, to the source folder for convenience. You can find the solutions in this project.

Solution

Here’s the sample input:

1721
979
366
299
675
1456

First, we need to read and parse the input. We can use the Kotlin readLines() function for reading a list of lines from a given file:

File("src/day1/input.txt").readLines()

The readLines() returns a list of Strings, and we convert it to a list of numbers:

import java.io.File
fun main() {
   val numbers = File("src/day1/input.txt")
       .readLines()
       .map(String::toInt)


}

You put this code inside the main function, the entry point for your program. When you start typing, IntelliJ IDEA imports the java.io.File automatically.

Now we can simply iterate through the list, and then for each number repeat the iteration and check the sum:

for (first in numbers) {
   for (second in numbers) {
       if (first + second == 2020) {
           println(first * second)
           return
       }
   }
}

You put this code inside main, so return returns from main when the required numbers are found.

In a similar way, you check the sum of three numbers:

for (first in numbers) {
   for (second in numbers) {
       for (third in numbers) {
           if (first + second + third == 2020) {
               println(first * second * third)
               return
           }
       }
   }
}

You can run it and get the result for a given input. That’s it! The first task is really a simple one.

However, we iterate over the same list again and again for each of the elements. Having two nested loops for finding two numbers makes it N2 operations, where N is the number of elements. When we need to find three numbers, that’s three nested loops, and N3 operations. If the list of numbers is large, that’s not the most efficient way to solve this type of problem. Surely there is a better way, right?

There definitely is and the Kotlin standard library can help us express that concisely. As often happens, we can replace the long calculation with some kind of smart storage used to find the result.

Solving the task for two numbers

First, let’s build a map for number “complements” – numbers that together with the given number sum up to 2020:

val complements = numbers.associateBy { 2020 - it }

We use the Kotlin associateBy function to build the map. Its lambda argument returns a key in this map, by which the list element is getting stored. For the sample input it’ll be the following map:

numbers: [1721, 979, 366, 299, 675, 1456]
complements map: {299=1721, 1041=979, 1654=366, 1721=299, 1345=675, 564=1456}

After this procedure, you can clearly see the answer! The very first number 1721 from the list is present in the complements map as a key: 1721=299, which means it’s the complement for the number 299, and they sum to 2020.

Having stored this information in a map, we can check if any number from the list has a complement in this map. The following code finds the first number with an existing complement:

val pair = numbers.mapNotNull { number ->
   val complement = complements[number]
   if (complement != null) Pair(number, complement) else null
}.firstOrNull()

We transform each number into a pair consisting of the number and its complement (if the complement exists) and then find the first non-null result.

We use mapNotNull, which transforms each element in a list and filters out all the resulting nulls. It’s shorthand for calling first map, and then filterNotNull on the result.

firstOrNull returns the first element in the list or null if the list is empty. Kotlin standard library often uses the OrNull suffix to mark functions returning null on failure rather than throwing an exception (like elementAtOrNull, singleOrNull, or maxOrNull).

Starting with Kotlin 1.5.0, you can also replace the two consequent operations mapNotNull and first(OrNull) with one function call: firstNotNullOf(OrNull).

After building the auxiliary structure, we managed to find the resulting two numbers in N operations, not in N2 as before!

We need a multiplication of these numbers, so here’s the last step:

println(pair?.let { (a, b) -> a * b })

The pair variable contains a nullable Pair of two numbers and is null if the initial list contains no numbers that sum up to 2020. We use safe access ?. together with the let function and destructuring in a lambda syntax to display the result in case pair is not null.

Solving the task for three numbers

The next step is solving this problem for three numbers. Let’s reuse what we’ve done so far and extract the logic of finding a pair of numbers summing up to a given number into a separate function:

fun List<Int>.findPairOfSum(sum: Int): Pair<Int, Int>? {
   // Map: sum - x -> x
   val complements = associateBy { sum - it }
   return firstNotNullOfOrNull { number ->
       val complement = complements[number]
       if (complement != null) Pair(number, complement) else null
   }
}

We also used the firstNotNullOfOrNull function.

Now, we use findPairOfSum to build a helper map that stores the complement pair of values for each number which together with this number sums up to 2020:

// Map: x -> (y, z) where y + z = 2020 - x
val complementPairs: Map<Int, Pair<Int, Int>?> =
   numbers.associateWith { numbers.findPairOfSum(2020 - it) }

For the same initial input, here’s the complement pairs map:

numbers: [1721, 979, 366, 299, 675, 1456]
complement pairs: {1721=null, 979=(366, 675), 366=(979, 675), 299=null, 675=(979, 366), 1456=null}

As before, you already can see the answer! It’s the number that corresponds to a non-null pair in a map.

However, we don’t really need to build the whole map — we only need to find the first number that corresponds to a non-null pair! Let’s find it using the already familiar firstNotNullOfOrNull function:

fun List<Int>.findTripleOfSum(): Triple<Int, Int, Int>? =
   firstNotNullOfOrNull { x ->
       findPairOfSum(2020 - x)?.let { pair ->
           Triple(x, pair.first, pair.second)
       }
   }

Note Kotlin’s concise syntax – the function can return an expression directly.

The final step is to find the multiplication if the resulting triple is non-null, similar to how we did it before:

println(triple?.let { (x, y, z) -> x * y * z} )

That’s all!

In the next part, we’ll discuss how to solve the second task. Please let us know if you find this content useful and would like us to provide solutions for more tasks!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingSolving Advent of Code Puzzles in Idiomatic Kotlin

End of content

No more pages to load