Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Project: Todo App

Step 1: Set Up Your Scala Project

First, ensure you have Scala and SBT (Scala Build Tool) installed on your machine. You can check by running the following commands:

scala -version
sbt -version

If you don't have them installed, follow the installation instructions from the official Scala website.

Next, in IntelliJ create a new SBT project

Step 2: Define the To-Do App Structure

Create a Scala object to hold your application logic. You can do this by creating a new file in the src/main/scala directory. Let's call it TodoApp.scala.

import scala.io.StdIn.readLine
import scala.collection.mutable.ListBuffer

object TodoApp:

  case class Task(id: Int, description: String)

  val tasks: ListBuffer[Task] = ListBuffer.empty
  var nextId: Int = 1

  def main(args: Array[String]): Unit = 
    var continue = true

    while (continue) 
      println("\nTODO App")
      println("1. Add Task")
      println("2. List Tasks")
      println("3. Delete Task")
      println("4. Exit")
      print("Choose an option: ")

      readLine() match {
        case "1" => addTask()
        case "2" => listTasks()
        case "3" => deleteTask()
        case "4" => continue = false
        case _ => println("Invalid option. Please try again.")


  def addTask(): Unit = 
    print("Enter task description: ")
    val description = readLine()
    tasks += Task(nextId, description)
    nextId += 1
    println(s"Task added with id $nextId")
  

  def listTasks(): Unit = 
    if tasks.isEmpty {
      println("No tasks available.")
    } else {
      tasks.foreach(task => println(s"${task.id}. ${task.description}"))
    }

  def deleteTask(): Unit = 
    print("Enter task id to delete: ")
    val id = readLine().toInt
    val taskIndex = tasks.indexWhere(_.id == id)
    if taskIndex != -1 {
      tasks.remove(taskIndex)
      println(s"Task with id $id deleted.")
    } else {
      println(s"Task with id $id not found.")
    }
  
}

Step 3: Running Your Application

To run your application, use SBT:

sbt run

This command compiles and runs your Scala application. You should see the menu and be able to interact with your to-do list by adding, listing, and deleting tasks.

Explanation

  1. Task Case Class: This defines a simple structure to hold task data.
  2. tasks ListBuffer: A mutable list to hold the tasks.
  3. nextId: A counter to assign unique IDs to tasks.
  4. main Method: This is the entry point of the application. It shows the menu and reads user input.
  5. addTask, listTasks, deleteTask Methods: These methods handle adding, listing, and deleting tasks respectively.

Add Priority

To add a priority to each task and sort the list based on priority, we need to modify the Task case class and the listTasks method. We will also adjust the addTask method to accept priority input from the user.

Here's the updated code for the TodoApp:

import scala.io.StdIn.readLine
import scala.collection.mutable.ListBuffer

object TodoApp:

  case class Task(id: Int, description: String, priority: Int)

  val tasks: ListBuffer[Task] = ListBuffer.empty
  var nextId: Int = 1

  def main(args: Array[String]): Unit = 
    var continue = true

    while (continue) 
      println("\nTODO App")
      println("1. Add Task")
      println("2. List Tasks")
      println("3. Delete Task")
      println("4. Exit")
      print("Choose an option: ")

      readLine() match 
        case "1" => addTask()
        case "2" => listTasks()
        case "3" => deleteTask()
        case "4" => continue = false
        case _ => println("Invalid option. Please try again.")

  def addTask(): Unit = 
    print("Enter task description: ")
    val description = readLine()
    print("Enter task priority (1=High, 2=Medium, 3=Low): ")
    val priority = readLine().toInt

    tasks += Task(nextId, description, priority)
    nextId += 1
    println(s"Task added with id $nextId and priority $priority")
  

  def listTasks(): Unit = 
    if tasks.isEmpty {
      println("No tasks available.")
    } else {
      println("Tasks (sorted by priority):")
      val sortedTasks = tasks.sortBy(_.priority)
      sortedTasks.foreach(task => println(s"${task.id}. [Priority: ${task.priority}] ${task.description}"))
    }

  def deleteTask(): Unit = 
    print("Enter task id to delete: ")
    val id = readLine().toInt
    val taskIndex = tasks.indexWhere(_.id == id)
    if taskIndex != -1 {
      tasks.remove(taskIndex)
      println(s"Task with id $id deleted.")
    } else {
      println(s"Task with id $id not found.")
    }

Explanation of Changes

  1. Task Case Class: Added a new field priority to the Task case class.
  2. addTask Method: Now asks the user to input a priority level for the task.
  3. listTasks Method: Sorts tasks by their priority before printing. Lower numbers represent higher priority (1=High, 2=Medium, 3=Low).

Add User

To add a User case class and allow associating tasks with specific users, we need to make some adjustments to our application. We'll introduce a user management system where users can be added, and tasks can be associated with users. This will include methods to add users, list users, and associate tasks with users.

Here is the updated code:

import scala.io.StdIn.readLine
import scala.collection.mutable.{ListBuffer, Map}

object TodoApp:

  case class Task(id: Int, description: String, priority: Int, userId: Int)
  case class User(id: Int, name: String)
  
  val tasks: ListBuffer[Task] = ListBuffer.empty
  val users: ListBuffer[User] = ListBuffer.empty
  var nextTaskId: Int = 1
  var nextUserId: Int = 1

  def main(args: Array[String]): Unit =
    var continue = true
    
    while (continue)
      println("\nTODO App")
      println("1. Add User")
      println("2. List Users")
      println("3. Add Task")
      println("4. List Tasks")
      println("5. Delete Task")
      println("6. Exit")
      print("Choose an option: ")
    
    readLine() match
      case "1" => addUser()
      case "2" => listUsers()
      case "3" => addTask()
      case "4" => listTasks()
      case "5" => deleteTask()
      case "6" => continue = false
      case _ => println("Invalid option. Please try again.")
  
  def addUser(): Unit =
    print("Enter user name: ")
    val name = readLine()
    users += User(nextUserId, name)
    println(s"User added with id $nextUserId and name $name")
    nextUserId += 1
  
  
  def listUsers(): Unit =
    if users.isEmpty then
      println("No users available.")
    else
      println("Users:")
    users.foreach(user => println(s"${user.id}. ${user.name}"))
  
  def addTask(): Unit =
    print("Enter task description: ")
    val description = readLine()
    print("Enter task priority (1=High, 2=Medium, 3=Low): ")
    val priority = readLine().toInt
    print("Enter user id: ")
    val userId = readLine().toInt
    
    if users.exists(_.id == userId) then
      tasks += Task(nextTaskId, description, priority, userId)
      println(s"Task added with id $nextTaskId, priority $priority, assigned to user $userId")
      nextTaskId += 1
    else
      println(s"User with id $userId does not exist.")
  
  
  def listTasks(): Unit =
    if tasks.isEmpty then
      println("No tasks available.")
    else
      println("Tasks (sorted by priority):")
    val sortedTasks = tasks.sortBy(_.priority)
    sortedTasks.foreach(task => {
      val user = users.find(_.id == task.userId).map(_.name).getOrElse("Unknown User")
      println(s"${task.id}. [Priority: ${task.priority}] ${task.description} (Assigned to: $user)")
    })
  
  
  def deleteTask(): Unit =
    print("Enter task id to delete: ")
    val id = readLine().toInt
    val taskIndex = tasks.indexWhere(_.id == id)
    if taskIndex != -1 then
      tasks.remove(taskIndex)
      println(s"Task with id $id deleted.")
    else
      println(s"Task with id $id not found.")

Explanation of Changes

  1. User Case Class: Added a User case class with id and name fields.
  2. Task Case Class: Added a userId field to associate tasks with a specific user.
  3. users ListBuffer: A mutable list to hold the users.
  4. nextUserId: A counter to assign unique IDs to users.
  5. addUser Method: Allows adding a new user.
  6. listUsers Method: Lists all users.
  7. addTask Method: Now asks for a userId to assign the task to a specific user.
  8. listTasks Method: Displays tasks with associated user names.