Wednesday, July 31, 2019

Kotlin in Action: Chapter 7 Exercises

Starting with chapter 7 the Kotlin in Action book starts moving on to more advanced topics that may or may not have direct correlations with the Java language, so for the rest of the chapters in the book I will present a problem to solve instead of code to convert. So without further ado, here's the exercise for chapter 7:

Exercise: Operator Overloads for Json Manipulation

This will be yet another problem involving json (What can I say? I'm a web developer and it's kind of relevant to web development.), but to mix things up, we'll work with the Vertx library's JsonObject and JsonArray this time. So you'll want to add these two dependencies to your build.gradle file:
compile group: 'io.vertx', name: 'vertx-core', version: '3.8.0'
compile group: 'io.vertx', name: 'vertx-lang-kotlin', version: '3.8.0'
From here there are two paths that can be explored: one where the operator overloads that we write treat the JsonObject or JsonArray as immutable, and the other where the operator overloads that we write treat the JsonObject or JsonArray as mutable. Note that these two approaches don't play well together, and should be explored separately.

Immutable

In the immutable scenario, we'll want to overload the plus and minus operators for both JsonObject and JsonArray. In all cases the JsonObjects and/or JsonArrays won't be modified, but a new one will be created and returned instead. When adding two JsonObjects together, the fields from both JsonObjects will be added to the new JsonObject. When there are overlapping fields, the fields from the latter JsonObject will take precedence. When a JsonObject and a Pair are added together, then the Pair is added as a field on the JsonObject, or replaces an existing field if there's overlap. Subtracting a String from a JsonObject will subtract the field with that key from the JsonObject. Subtracting a Collection of Strings from a JsonObject will remove all fields that have a corresponding key.

Adding two JsonArrays should return a JsonArray with all the values from both JsonArrays, with the values from the former JsonArray being first and the values from the latter JsonArray being last. Adding any object to the JsonArray will append that object to the end of the JsonArray. Subtracting any object from a JsonArray will remove the firstinstance of that object from the JsonArray. Subtracting one JsonArray from another will remove all of the values in the second JsonArray from the first JsonArray. Something to explore would be whether or not it would be meaningful to implement the operator such that subtracting an int from a JsonArray will remove the element at that index.

Mutable

This should follow the same general rules as defined by the immutable problem above except that it will modify the first JsonObject or JsonArray. This will implement the += and -= operators directly, and not implement the + or - operators.

As usual, a solution will be posted in a followup update. And here's the answers.

Wednesday, July 24, 2019

Kotlin in Action: Answers to Chapter 6 Exercises

And here's the answer to the chapter 6 exercise.

Exercise 1: Promo Emails

import java.lang.Exception
import java.lang.RuntimeException

fun main() {
  val jsmith432 = User(
    "jsmith432",
    "John",
    "Smith",
    "john.smith@yahoo.com"
  )
  val jdoe = User(
    username = "jdoe",
    emailAddress = "jdoe@gmail.com"
  )
  val unknown = User(emailAddress = "abc123@hotmail.com")
  val bob = User(
    username = "bob",
    firstName = "Bob"
  )
  val users = listOf(jsmith432, jdoe, unknown, bob)
  val usersWithNull = listOf(jsmith432, null, jdoe)

  try {
    sendSalesPromotion(null)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  sendSalesPromotion(jsmith432)
  sendSalesPromotion(jdoe)
  sendSalesPromotion(unknown)
  sendSalesPromotion(bob)

  try {
    sendSalesPromotions(null)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  try {
    sendSalesPromotions(usersWithNull)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  sendSalesPromotions(users)
}

fun sendSalesPromotions(users: List<User?>?) {
  users ?: nullParameterException("users")
  users.forEach { it ?: nullParameterException("user") }
  users.forEach(::sendSalesPromotion)
}

fun sendSalesPromotion(user: User?) {
  user ?: nullParameterException("user")
  user.emailAddress?.let {
    val subject = "Blowout XYZ Widget Sale!"
    val message =
      """Dear ${user.firstName ?: user.username ?: "Valued Customer"}
        |
        |We're having a massive sale on XYZ Widgets!
        |95% Off! Get yours today!""".trimMargin()
    sendEmail(Email(it, subject, message))
  }
}

fun nullParameterException(paramName: String): Nothing {
  throw RuntimeException("Param $paramName is null")
}

fun sendEmail(email: Email) {
  println("""Email sent:
            |  emailAddress: ${email.emailAddress}
            |  subject: ${email.subject}
            |  message: ${email.message}
            |""".trimMargin()
  )
}

data class User(
  val username: String? = null,
  val firstName: String? = null,
  val lastName: String? = null,
  val emailAddress: String? = null
)

data class Email(
  val emailAddress: String,
  val subject: String,
  val message: String
)

Tuesday, July 23, 2019

Kotlin in Action: Chapter 6 Exercises

Here's a practice problem for chapter 6.

Exercise 1: Promo Emails

In this example we have multiple users and we want to send a promo email advertising a sale to them (for simplicity's sake, sending an email will just be printing to the console), so long as they have an email on record. If we have the user's first name, then the email will address them by first name. Otherwise we will address them using their username, so long as we have that. Barring that, we will simply address them as "Valued Customer".

If a user is null, or a list of users is null, or a user within a list of users is null, then we will throw an exception. (This is a bit contrived, I know, but it allows us to better test out a few things from chapter 6.)

Note that this conversion will primarily focus on converting the sendSalesPromotion and sendSalesPromotions methods and supporting methods. As such I will provide a partial Kotlin conversion that converts everything except these methods and their supporting methods.
import java.util.ArrayList;
import java.util.List;

public class JavaExample {

  public static void main(String[] args) {
    User jsmith432 = new User("jsmith432",
                              "John",
                              "Smith",
                              "john.smith@yahoo.com");
    User jdoe = new User("jdoe",
                         null,
                         null,
                         "jdoe@gmail.com");
    User unknown = new User(null,
                            null,
                            null,
                            "abc123@hotmail.com");
    User bob = new User("bob",
                        "Bob",
                        null,
                        null);
    List<User> users = new ArrayList<>();
    users.add(jsmith432);
    users.add(jdoe);
    users.add(unknown);
    users.add(bob);
    List<User> usersWithNull = new ArrayList<>();
    usersWithNull.add(jsmith432);
    usersWithNull.add(null);
    usersWithNull.add(jdoe);

    try {
      sendSalesPromotion(null);
    } catch (Exception e) {
      System.out.println(e.getMessage() + "\n");
    }

    sendSalesPromotion(jsmith432);
    sendSalesPromotion(jdoe);
    sendSalesPromotion(unknown);
    sendSalesPromotion(bob);

    try {
      sendSalesPromotions(null);
    } catch (Exception e) {
      System.out.println(e.getMessage() + "\n");
    }

    try {
      sendSalesPromotions(usersWithNull);
    } catch (Exception e) {
      System.out.println(e.getMessage() + "\n");
    }

    sendSalesPromotions(users);
  }

  public static void sendSalesPromotions(List<User> users) {
    if (users != null) {
      if(users.stream().anyMatch(user -> user == null)) {
        nullParameterException("user");
      }
      users.forEach(JavaExample::sendSalesPromotion);
    } else {
      nullParameterException("users");
    }
  }

  public static void sendSalesPromotion(User user) {
    if (user != null) {
      if (user.getEmailAddress() != null) {
        String emailAddress = user.getEmailAddress();
        String subject = "Blowout XYZ Widget Sale!";
        String message = "Dear " +
            (user.getFirstName() != null ?
                user.getFirstName() :
                user.getUsername() != null ?
                    user.getUsername() :
                    "Valued Customer") + "\n\n" +
            "We're having a massive sale on XYZ Widgets!\n" +
            "95% Off! Get yours today!";
        sendEmail(new Email(emailAddress, subject, message));
      }
    } else {
      nullParameterException("user");
    }
  }

  public static void nullParameterException(String paramName) {
    throw new RuntimeException("Param " + paramName + " is null");
  }

  public static void sendEmail(Email email) {
    System.out.println("Email sent:\n" +
        "  emailAddress: " + email.getEmailAddress() + "\n" +
        "  subject: " + email.getSubject() + "\n" +
        "  message: " + email.getMessage() + "\n");
  }
}

public class User {

  private final String username;
  private final String firstName;
  private final String lastName;
  private final String emailAddress;

  public User(String username,
              String firstName,
              String lastName,
              String emailAddress) {
    this.username = username;
    this.firstName = firstName;
    this.lastName = lastName;
    this.emailAddress = emailAddress;
  }

  public String getUsername() {
    return username;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getEmailAddress() {
    return emailAddress;
  }
}

public class Email {
  private final String emailAddress;
  private final String subject;
  private final String message;

  public Email(String emailAddress,
               String subject,
               String message) {
    this.emailAddress = emailAddress;
    this.subject = subject;
    this.message = message;
  }

  public String getEmailAddress() {
    return emailAddress;
  }

  public String getSubject() {
    return subject;
  }

  public String getMessage() {
    return message;
  }
}
And here's the partial Kotlin conversion
import java.lang.Exception

fun main() {
  val jsmith432 = User("jsmith432",
                       "John",
                       "Smith",
                       "john.smith@yahoo.com")
  val jdoe = User(username = "jdoe",
                  emailAddress = "jdoe@gmail.com")
  val unknown = User(emailAddress = "abc123@hotmail.com")
  val bob = User(username = "bob",
                 firstName = "Bob")
  val users = listOf(jsmith432, jdoe, unknown, bob)
  val usersWithNull = listOf(jsmith432, null, jdoe)

  try {
    sendSalesPromotion(null)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  sendSalesPromotion(jsmith432)
  sendSalesPromotion(jdoe)
  sendSalesPromotion(unknown)
  sendSalesPromotion(bob)

  try {
    sendSalesPromotions(null)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  try {
    sendSalesPromotions(usersWithNull)
  } catch (e: Exception) {
    println("${e.message}\n")
  }

  sendSalesPromotions(users)
}

data class User(val username: String? = null,
                val firstName: String? = null,
                val lastName: String? = null,
                val emailAddress: String? = null)

data class Email(val emailAddress: String,
                 val subject: String,
                 val message: String)
The answer can be found here.

Wednesday, July 17, 2019

Kotlin in Action: Answers to Chapter 5 Exercises

So here are the answers to the last post.

Exercise 1: Json Manipulation

import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser

fun main() {
  val usersJson = """[
                    |  {
                    |    "id": "543",
                    |    "username": "john123",
                    |    "firstName": "John",
                    |    "lastName": "Smith",
                    |    "email": "john@smith.com"
                    |  },   {
                    |    "id": "438",
                    |    "username": "janedoe5",
                    |    "firstName": "Jane",
                    |    "lastName": "Doe",
                    |    "email": "jane.doe@gmail.com"
                    |  }
                    |]""".trimMargin()
  val booksJson = """[
                    |  {"id": "1", "title": "Kotlin in Action"},
                    |  {"id": "2", "title": "Kotlin in Action"},
                    |  {"id": "3", "title": "Learning RxJava"},
                    |  {"id": "4", "title": "Refactoring"},
                    |  {"id": "5", "title": "Grokking Algorithms"},
                    |  {"id": "6", "title": "Code Complete"}
                    |]""".trimMargin()
  val checkoutsJson = """[
                        |  {"userId": "543", "bookId": "1"},
                        |  {"userId": "543", "bookId": "5"},
                        |  {"userId": "438", "bookId": "2"},
                        |  {"userId": "438", "bookId": "3"}
                        |]""".trimMargin()

  val parser = JsonParser()
  val users = parser.parse(usersJson).asJsonArray
  val books = parser.parse(booksJson).asJsonArray
  val checkouts = parser.parse(checkoutsJson).asJsonArray

  val userCheckouts = users.map { it.asJsonObject }
    .flatMap { user -> checkouts.map { it.asJsonObject }
      .filter { checkout -> user["id"] == checkout["userId"] }
      .map { checkout -> JsonObject().apply {
        addProperty("firstName", user["firstName"].asString)
        addProperty("lastName", user["lastName"].asString)
        addProperty("bookId", checkout["bookId"].asString)
      } }
    }
    .flatMap { userCheckout -> books.map { it.asJsonObject }
      .filter { book -> userCheckout["bookId"] == book["id"] }
      .map { book -> userCheckout.deepCopy().apply {
        addProperty("title", book["title"].asString)
        remove("bookId")
      } }
    }
    .fold(JsonArray()) { array, obj -> array.apply { add(obj) }}
  println(userCheckouts)
}
Now honestly the java code wasn't very clean and readable to start with, so if we wanted to clean it up a little we could do the following:
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser

fun main() {
  val usersJson = """[
                    |  {
                    |    "id": "543",
                    |    "username": "john123",
                    |    "firstName": "John",
                    |    "lastName": "Smith",
                    |    "email": "john@smith.com"
                    |  },   {
                    |    "id": "438",
                    |    "username": "janedoe5",
                    |    "firstName": "Jane",
                    |    "lastName": "Doe",
                    |    "email": "jane.doe@gmail.com"
                    |  }
                    |]""".trimMargin()
  val booksJson = """[
                    |  {"id": "1", "title": "Kotlin in Action"},
                    |  {"id": "2", "title": "Kotlin in Action"},
                    |  {"id": "3", "title": "Learning RxJava"},
                    |  {"id": "4", "title": "Refactoring"},
                    |  {"id": "5", "title": "Grokking Algorithms"},
                    |  {"id": "6", "title": "Code Complete"}
                    |]""".trimMargin()
  val checkoutsJson = """[
                        |  {"userId": "543", "bookId": "1"},
                        |  {"userId": "543", "bookId": "5"},
                        |  {"userId": "438", "bookId": "2"},
                        |  {"userId": "438", "bookId": "3"}
                        |]""".trimMargin()

  val parser = JsonParser()
  val users = parser.parse(usersJson).asJsonArray
  val books = parser.parse(booksJson).asJsonArray
  val checkouts = parser.parse(checkoutsJson).asJsonArray

  val userCheckouts = users.map { it.asJsonObject }
    .flatMap { user -> findUserCheckouts(user, checkouts)
      .map { checkout -> buildUserCheckout(user, checkout) }
    }
    .flatMap { userCheckout -> findCheckoutBooks(userCheckout, books)
      .map { book -> swapBookIdForTitleOnUserCheckout(userCheckout, book) }
    }
    .toJsonArray()
  println(userCheckouts)
}

private fun findUserCheckouts(
  user: JsonObject,
  checkouts: JsonArray
): List =
  checkouts.map { it.asJsonObject }
    .filter { checkout -> user["id"] == checkout["userId"] }

private fun buildUserCheckout(
  user: JsonObject,
  checkout: JsonObject
): JsonObject =
  JsonObject().apply {
    addProperty("firstName", user["firstName"].asString)
    addProperty("lastName", user["lastName"].asString)
    addProperty("bookId", checkout["bookId"].asString)
  }

private fun findCheckoutBooks(
  userCheckout: JsonObject,
  books: JsonArray
): List =
  books.map { it.asJsonObject }
    .filter { book -> userCheckout["bookId"] == book["id"] }

private fun swapBookIdForTitleOnUserCheckout(
  userCheckout: JsonObject,
  book: JsonObject
): JsonObject =
  userCheckout.deepCopy().apply {
    addProperty("title", book["title"].asString)
  }

private fun List<JsonObject>.toJsonArray(): JsonArray =
    fold(JsonArray()) { array, obj -> array.apply { add(obj) }}

Kotlin in Action: Chapter 5 Exercises

Just like the last few weeks, here's a practice problem, this time for chapter 5.

Exercise 1: Json Manipulation

In this example we will continue using the Gson library, We will manipulate multiple JsonArrays to return a list mapping the user's first name and last name to the titles of the books that the user has checked out.
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import static java.util.stream.StreamSupport.stream;

public class JavaExample {

  public static void main(String[] args) {
    String usersJson = "[\n" +
                       "  {\n" +
                       "    \"id\": \"543\",\n" +
                       "    \"username\": \"john123\",\n" +
                       "    \"firstName\": \"John\",\n" +
                       "    \"lastName\": \"Smith\",\n" +
                       "    \"email\": \"john@smith.com\"\n" +
                       "  }, " +
                       "  {\n" +
                       "    \"id\": \"438\",\n" +
                       "    \"username\": \"janedoe5\",\n" +
                       "    \"firstName\": \"Jane\",\n" +
                       "    \"lastName\": \"Doe\",\n" +
                       "    \"email\": \"jane.doe@gmail.com\"\n" +
                       "  }\n" +
                       "]";
    String booksJson = "[\n" +
                       "  {\"id\": \"1\", \"title\": \"Kotlin in Action\"},\n" +
                       "  {\"id\": \"2\", \"title\": \"Kotlin in Action\"},\n" +
                       "  {\"id\": \"3\", \"title\": \"Learning RxJava\"},\n" +
                       "  {\"id\": \"4\", \"title\": \"Refactoring\"},\n" +
                       "  {\"id\": \"5\", \"title\": \"Grokking Algorithms\"},\n" +
                       "  {\"id\": \"6\", \"title\": \"Code Complete\"}\n" +
                       "]";
    String checkoutsJson = "[\n" +
                           "  {\"userId\": \"543\", \"bookId\": \"1\"},\n" +
                           "  {\"userId\": \"543\", \"bookId\": \"5\"},\n" +
                           "  {\"userId\": \"438\", \"bookId\": \"2\"},\n" +
                           "  {\"userId\": \"438\", \"bookId\": \"3\"}\n" +
                           "]";

    JsonParser parser = new JsonParser();
    JsonArray users = parser.parse(usersJson).getAsJsonArray();
    JsonArray books = parser.parse(booksJson).getAsJsonArray();
    JsonArray checkouts = parser.parse(checkoutsJson).getAsJsonArray();

    JsonArray userCheckouts = stream(users.spliterator(), false)
        .map(JsonElement::getAsJsonObject)
        .flatMap(user -> stream(checkouts.spliterator(), false)
            .map(JsonElement::getAsJsonObject)
            .filter(checkout -> user.get("id").getAsString().equals(
                checkout.get("userId").getAsString()))
            .map(checkout -> {
              JsonObject jo = new JsonObject();
              jo.addProperty("firstName", user.get("firstName").getAsString());
              jo.addProperty("lastName", user.get("lastName").getAsString());
              jo.addProperty("bookId", checkout.get("bookId").getAsString());
              return jo;
            }))
        .flatMap(userCheckout -> stream(books.spliterator(), false)
            .map(JsonElement::getAsJsonObject)
            .filter(book -> userCheckout.get("bookId").getAsString().equals(
                book.get("id").getAsString()))
            .map(book -> {
              JsonObject jo = userCheckout.deepCopy();
              jo.addProperty("title", book.get("title").getAsString());
              jo.remove("bookId");
              return jo;
            }))
        .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);

    System.out.println(userCheckouts);
  }
}
Similar to previous weeks, an answer will be shared in a follow up post. (And the answer can be found here.)

Wednesday, July 10, 2019

Kotlin in Action: Answers to Chapter 4 Exercises

So here are the answers to the last post:

Exercise 1: Json to Pojo and Back Again

import com.google.gson.JsonObject
import com.google.gson.JsonParser

fun main() {
  val johnJson = """{
                   |  "username": "john123",
                   |  "firstName": "John",
                   |  "lastName": "Smith",
                   |  "email": "john@smith.com"
                   |}""".trimMargin()
  val janeJson = """{
                   |  "username": "janedoe5",
                   |  "firstName": "Jane",
                   |  "lastName": "Doe",
                   |  "email": "jane.doe@gmail.com"
                   |}""".trimMargin()

  val parser = JsonParser()
  val johnJsonObject = parser.parse(johnJson).asJsonObject
  val janeJsonObject = parser.parse(janeJson).asJsonObject

  val john1 = User.fromJson(johnJsonObject)
  val john2 = User.fromJson(johnJsonObject)
  val jane = User.fromJson(janeJsonObject)

  println(john1)
  println(john2)
  println(jane)

  println("john1 = john2: " + (john1 == john2))
  println("john1 = jane: " + (john1 == jane))

  val usersSet = hashSetOf(john1, john2, jane)

  println("HashSet size (expected 2): " + usersSet.size)

  val johnFinal = john1.toJson()
  val janeFinal = jane.toJson()

  println(johnFinal)
  println(janeFinal)
}

data class User(val username: String,
                val firstName: String,
                val lastName: String,
                val email: String) {
  companion object {
    fun fromJson(jsonObject: JsonObject) : User =
      User(jsonObject.get("username").asString,
             jsonObject.get("firstName").asString,
             jsonObject.get("lastName").asString,
             jsonObject.get("email").asString)
  }

  fun toJson() : JsonObject {
    val jsonObject = JsonObject()
    jsonObject.addProperty("username", username)
    jsonObject.addProperty("firstName", firstName)
    jsonObject.addProperty("lastName", lastName)
    jsonObject.addProperty("email", email)
    return jsonObject
  }
}
Or, alternatively, if you prefer to pull the json processing out into extension functions:
import com.google.gson.JsonObject
import com.google.gson.JsonParser

fun main() {
  val johnJson = """{
                   |  "username": "john123",
                   |  "firstName": "John",
                   |  "lastName": "Smith",
                   |  "email": "john@smith.com"
                   |}""".trimMargin()
  val janeJson = """{
                   |  "username": "janedoe5",
                   |  "firstName": "Jane",
                   |  "lastName": "Doe",
                   |  "email": "jane.doe@gmail.com"
                   |}""".trimMargin()

  val parser = JsonParser()
  val johnJsonObject = parser.parse(johnJson).asJsonObject
  val janeJsonObject = parser.parse(janeJson).asJsonObject

  val john1 = User.fromJson(johnJsonObject)
  val john2 = User.fromJson(johnJsonObject)
  val jane = User.fromJson(janeJsonObject)

  println(john1)
  println(john2)
  println(jane)

  println("john1 = john2: " + (john1 == john2))
  println("john1 = jane: " + (john1 == jane))

  val usersSet = hashSetOf(john1, john2, jane)

  println("HashSet size (expected 2): " + usersSet.size)

  val johnFinal = john1.toJson()
  val janeFinal = jane.toJson()

  println(johnFinal)
  println(janeFinal)
}

data class User(val username: String,
                val firstName: String,
                val lastName: String,
                val email: String) {
  companion object
}

fun User.Companion.fromJson(jsonObject: JsonObject) : User =
  User(jsonObject.get("username").asString,
    jsonObject.get("firstName").asString,
    jsonObject.get("lastName").asString,
    jsonObject.get("email").asString)

fun User.toJson() : JsonObject {
  val jsonObject = JsonObject()
  jsonObject.addProperty("username", username)
  jsonObject.addProperty("firstName", firstName)
  jsonObject.addProperty("lastName", lastName)
  jsonObject.addProperty("email", email)
  return jsonObject
}

Kotlin in Action: Chapter 4 Exercises

Alright, so just like last week, I'm posting a Java to Kotlin conversion exercise where the Java code will be posted now and the Kotlin code will be posted in a follow up post. After reading through chapter 4 of Kotlin in Action, you should be prepared to complete the exercise.

Exercise 1: Json to Pojo and Back Again

Taking advantage of the Gson library, we'll convert some json into JsonObjects, which we will then take and convert into User pojos, run some simple tests, and then convert the pojos back to JsonObjects again. (I know that Gson supports converting json directly to pojos, but for the sake of the exercise, we'll ignore that and instead code it by hand.)
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.util.HashSet;

public class JavaExample3 {

  public static void main(String[] args) {
    String johnJson = "{\n" +
                      "  \"username\": \"john123\",\n" +
                      "  \"firstName\": \"John\",\n" +
                      "  \"lastName\": \"Smith\",\n" +
                      "  \"email\": \"john@smith.com\"\n" +
                      "}";
    String janeJson = "{\n" +
                      "  \"username\": \"janedoe5\",\n" +
                      "  \"firstName\": \"Jane\",\n" +
                      "  \"lastName\": \"Doe\",\n" +
                      "  \"email\": \"jane.doe@gmail.com\"\n" +
                      "}";

    JsonParser parser = new JsonParser();
    JsonObject johnJsonObject = parser.parse(johnJson).getAsJsonObject();
    JsonObject janeJsonObject = parser.parse(janeJson).getAsJsonObject();

    User john1 = User.fromJson(johnJsonObject);
    User john2 = User.fromJson(johnJsonObject);
    User jane = User.fromJson(janeJsonObject);

    System.out.println(john1);
    System.out.println(john2);
    System.out.println(jane);

    System.out.println("john1 = john2: " + john1.equals(john2));
    System.out.println("john1 = jane: " + john1.equals(jane));

    HashSet<User> usersSet = new HashSet<>();
    usersSet.add(john1);
    usersSet.add(john2);
    usersSet.add(jane);

    System.out.println("HashSet size (expected 2): " + usersSet.size());

    JsonObject johnFinal = john1.toJson();
    JsonObject janeFinal = jane.toJson();

    System.out.println(johnFinal);
    System.out.println(janeFinal);
  }
}

import com.google.gson.JsonObject;

import java.util.Objects;

public class User {

  public static User fromJson(JsonObject jsonObject) {
    return new User(jsonObject.get("username").getAsString(),
                    jsonObject.get("firstName").getAsString(),
                    jsonObject.get("lastName").getAsString(),
                    jsonObject.get("email").getAsString());
  }

  private final String username;
  private final String firstName;
  private final String lastName;
  private final String email;

  public User(String username,
              String firstName,
              String lastName,
              String email) {
    this.username = username;
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
  }

  public String getUsername() {
    return username;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getEmail() {
    return email;
  }

  @Override
  public String toString() {
    return "User(username=" + username +
        ", firstName=" + firstName +
        ", lastName=" + lastName +
        ", email=" + email + ")";
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof User) {
      User other = (User) obj;
      return equals(username, other.username) &&
          equals(firstName, other.firstName) &&
          equals(lastName, other.lastName) &&
          equals(email, other.email);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return Objects.hash(username, firstName, lastName, email);
  }

  private boolean equals(String str1, String str2) {
    return (str1 == null && str2 == null) ||
        (str1 != null && str1.equals(str2));
  }

  public JsonObject toJson() {
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("username", username);
    jsonObject.addProperty("firstName", firstName);
    jsonObject.addProperty("lastName", lastName);
    jsonObject.addProperty("email", email);
    return jsonObject;
  }
}
Answers can be found here.

Thursday, July 4, 2019

Kotlin in Action: Answers to Chapter 3 Exercises

In this post I gave a couple of Java to Kotlin conversion exercises. Here's the answers, or at least one version of the answers:

Exercise 1: Roman Numerals

import java.util.*

// This can only be used to test your results.
private val testList = listOf(
    "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
    "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX",
    "XX", "XXI", "XXII", "XXIII", "XXIV", "XXV", "XXVI", "XXVII",
    "XXVIII", "XXIX", "XXX", "XXXI", "XXXII", "XXXIII", "XXXIV",
    "XXXV", "XXXVI", "XXXVII", "XXXVIII", "XXXIX", "XL", "XLI",
    "XLII", "XLIII", "XLIV", "XLV", "XLVI", "XLVII", "XLVIII",
    "XLIX", "L", "LI", "LII", "LIII", "LIV", "LV", "LVI", "LVII",
    "LVIII", "LIX", "LX", "LXI", "LXII", "LXIII", "LXIV", "LXV",
    "LXVI", "LXVII", "LXVIII", "LXIX", "LXX", "LXXI", "LXXII",
    "LXXIII", "LXXIV", "LXXV", "LXXVI", "LXXVII", "LXXVIII",
    "LXXIX", "LXXX", "LXXXI", "LXXXII", "LXXXIII", "LXXXIV",
    "LXXXV", "LXXXVI", "LXXXVII", "LXXXVIII", "LXXXIX", "XC", "XCI",
    "XCII", "XCIII", "XCIV", "XCV", "XCVI", "XCVII", "XCVIII",
    "XCIX", "C")

private val romanNumeralMap = TreeMap(mapOf(
    1 to "I",
    4 to "IV",
    5 to "V",
    9 to "IX",
    10 to "X",
    40 to "XL",
    50 to "L",
    90 to "XC",
    100 to "C"))

fun main() {
    var passed = true
    for (i in 1..100) {
        val romanNumeral = i.toRomanNumeral()
        println(romanNumeral)
        passed = passed && romanNumeral == testList[i-1]
    }
    println(if (passed) "Passed" else "Failed")
}

fun Int.toRomanNumeral() : String {
    val floorKey = romanNumeralMap.floorKey(this)
    if (this == floorKey) {
        return romanNumeralMap[this]?:""
    }
    return (romanNumeralMap[floorKey]?:"") +
            (this - floorKey).toRomanNumeral()
}

Exercise 2: SQL Query String

private const val TABLE_NAME = "blog_posts"

fun main() {
    val query = """SELECT id, data
                  .FROM $TABLE_NAME
                  .WHERE data->>'userId' = ?""".trimMargin(".")
    println(query)
}

Wednesday, July 3, 2019

Kotlin in Action: Chapter 3 Exercises

I'm currently reading through the book Kotlin in Action, which is a great intro to Kotlin that assumes that you know Java and builds off of your Java knowledge. With that in mind, I thought it would be worthwhile to come up with a couple of test exercises as I go through the book, where you're given some code in Java, and you need to convert it to Kotlin (without using the Java to Kotlin auto converter). So here's a couple small exercises that you should be able to complete once you've gotten through chapter 3 of the book.

Exercise 1: Roman Numerals

This is a simple bit of code that takes the numbers 1 through 100 and converts them to Roman Numerals and print them to the console. It will also compare those Roman Numerals to a test list and print out a "Passed" or "Failed" depending on if the generated Roman Numerals match the list. (Code based loosely off of the stack overflow answer here.)

import java.util.List;
import java.util.TreeMap;

import static java.util.Arrays.asList;

public class JavaExample1 {

    // This can only be used to test your results.
    private static List<String> testList = asList(
        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
        "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX",
        "XX", "XXI", "XXII", "XXIII", "XXIV", "XXV", "XXVI", "XXVII",
        "XXVIII", "XXIX", "XXX", "XXXI", "XXXII", "XXXIII", "XXXIV",
        "XXXV", "XXXVI", "XXXVII", "XXXVIII", "XXXIX", "XL", "XLI",
        "XLII", "XLIII", "XLIV", "XLV", "XLVI", "XLVII", "XLVIII",
        "XLIX", "L", "LI", "LII", "LIII", "LIV", "LV", "LVI", "LVII",
        "LVIII", "LIX", "LX", "LXI", "LXII", "LXIII", "LXIV", "LXV",
        "LXVI", "LXVII", "LXVIII", "LXIX", "LXX", "LXXI", "LXXII",
        "LXXIII", "LXXIV", "LXXV", "LXXVI", "LXXVII", "LXXVIII",
        "LXXIX", "LXXX", "LXXXI", "LXXXII", "LXXXIII", "LXXXIV",
        "LXXXV", "LXXXVI", "LXXXVII", "LXXXVIII", "LXXXIX", "XC", "XCI",
        "XCII", "XCIII", "XCIV", "XCV", "XCVI", "XCVII", "XCVIII",
        "XCIX", "C");

    private static TreeMap<Integer, String> romanNumeralMap =
            new TreeMap<>();
    static {
        romanNumeralMap.put(1, "I");
        romanNumeralMap.put(4, "IV");
        romanNumeralMap.put(5, "V");
        romanNumeralMap.put(9, "IX");
        romanNumeralMap.put(10, "X");
        romanNumeralMap.put(40, "XL");
        romanNumeralMap.put(50, "L");
        romanNumeralMap.put(90, "XC");
        romanNumeralMap.put(100, "C");

    }

    public static void main(String[] args) {
        boolean passed = true;
        for (int i = 1; i <= 100; i++) {
            String romanNumeral = toRomanNumeral(i);
            System.out.println(romanNumeral);
            passed &= romanNumeral.equals(testList.get(i-1));
        }
        System.out.println(passed ? "Passed" : "Failed");
    }

    private static String toRomanNumeral(int num) {
        int floorKey = romanNumeralMap.floorKey(num);
        if (num == floorKey) {
            return romanNumeralMap.get(num);
        }
        return romanNumeralMap.get(floorKey) +
                toRomanNumeral(num - floorKey);
    }
}

Exercise 2: SQL Query String

This exercise simply has a basic pgSQL query in a string that pulls in the table name from a constant. Can you rework it to use a Kotlin multiline String?
public class JavaExample2 {

    private static final String TABLE_NAME = "blog_posts";

    public static void main(String[] args) {
        String query = "SELECT id, data \n" +
                "FROM " + TABLE_NAME + " \n" +
                "WHERE data->>'userId' = ?";
        System.out.println(query);
    }
}
Answers will be put out in a follow up blog post.

And here are the answers: Kotlin in Action: Answers to Chapter 3 Exercises