Skip to main content
Back to problems
#2590
Medium Algorithms

Design a todo list

Array Hash Table String Design Sorting
59.8% acceptance
Mar 31, 2026
48
15
Design a Todo List Where users can add tasks, mark them as complete, or get a list of pending tasks. Users can also add tags to tasks and can filter the tasks by certain tags. Implement the TodoList class: TodoList() Initializes the object. int addTask(int userId, String taskDescription, int dueDate, List tags) Adds a task for the user with the ID userId with a due date equal to dueDate and a list of tags attached to the task. The return value is the ID of the task. This ID starts at 1 and is sequentially increasing. That is, the first task's id should be 1, the second task's id should be 2, and so on. List getAllTasks(int userId) Returns a list of all the tasks not marked as complete for the user with ID userId, ordered by the due date. You should return an empty list if the user has no uncompleted tasks. List getTasksForTag(int userId, String tag) Returns a list of all the tasks that are not marked as complete for the user with the ID userId and have tag as one of their tags, ordered by their due date. Return an empty list if no such task exists. void completeTask(int userId, int taskId) Marks the task with the ID taskId as completed only if the task exists and the user with the ID userId has this task, and it is uncompleted.

Solution

Rust
Time O(n log n)
Space O(n)
LeetCode
solution.rs
use std::collections::HashMap;

struct Task {
  id: i32,
  description: String,
  due_date: i32,
  tags: Vec<String>,
  completed: bool,
}

struct TodoList {
  next_id: i32,
  user_tasks: HashMap<i32, Vec<Task>>,
}

impl TodoList {

  fn new() -> Self {
    TodoList { next_id: 1, user_tasks: HashMap::new() }
  }
  
  fn add_task(&mut self, user_id: i32, task_description: String, due_date: i32, tags: Vec<String>) -> i32 {
    let id = self.next_id;
    self.next_id += 1;
    self.user_tasks.entry(user_id).or_default().push(Task {
      id,
      description: task_description,
      due_date,
      tags,
      completed: false,
    });
    id
  }
  
  fn get_all_tasks(&self, user_id: i32) -> Vec<String> {
    if let Some(tasks) = self.user_tasks.get(&user_id) {
      let mut result: Vec<_> = tasks.iter()
        .filter(|t| !t.completed)
        .collect();
      result.sort_by_key(|t| t.due_date);
      result.iter().map(|t| t.description.clone()).collect()
    } else {
      vec![]
    }
  }
  
  fn get_tasks_for_tag(&self, user_id: i32, tag: String) -> Vec<String> {
    if let Some(tasks) = self.user_tasks.get(&user_id) {
      let mut result: Vec<_> = tasks.iter()
        .filter(|t| !t.completed && t.tags.contains(&tag))
        .collect();
      result.sort_by_key(|t| t.due_date);
      result.iter().map(|t| t.description.clone()).collect()
    } else {
      vec![]
    }
  }
  
  fn complete_task(&mut self, user_id: i32, task_id: i32) {
    if let Some(tasks) = self.user_tasks.get_mut(&user_id) {
      if let Some(task) = tasks.iter_mut().find(|t| t.id == task_id && !t.completed) {
        task.completed = true;
      }
    }
  }
}

/*
 * Your TodoList object will be instantiated and called as such:
 * let obj = TodoList::new();
 * let ret_1: i32 = obj.add_task(userId, taskDescription, dueDate, tags);
 * let ret_2: Vec<String> = obj.get_all_tasks(userId);
 * let ret_3: Vec<String> = obj.get_tasks_for_tag(userId, tag);
 * obj.complete_task(userId, taskId);
 */