I have a simple C++ program that manages as a todo list. The Todo, class contains a vector of Item to track each todo action and their id. The key constraint is that the ids must be kept strictly sequential and correspond to the order in the list.
My current solution uses a fix() member function in Todo that iterates across the vector of items to enforce the id constraint. Is there a more efficient way to make sure that the IDs in list are sequential, even after removing an element?
I'm looking for an answer that is objectively more efficient while still complying with the constraints of being based on vector, using class Item, and keeping track of IDs. Note that this isn't about using the IDs in the first place.
Example to illustrate the question
Imagine we have initialized an instance of Todo and have populated it with some items:
Todo todos = Todo();
todos.add("Take out the trash.");
todos.add("Do the laundry.");
todos.add("Read a book.");
todos.add("Make dinner.");
Our todo list has sequential IDs 0, 1, 2, 3, which we can check when we display it with list():
{Todo[{Item(id=0, desc="Take out the trash.")}, {Item(id=1, desc="Do the laundry.")}, {Item(id=2, desc="Read a book.")}, {Item(id=3, desc="Make dinner.")}]}
Let's remove the element at ID 2:
todos.remove(2);
The result are:
{Todo[{Item(id=0, desc="Take out the trash.")}, {Item(id=1, desc="Do the laundry.")}, {Item(id=3, desc="Make dinner.")}]}
The IDs are still ordered but there is a gap in the sequence: 0, 1, 3. I want them to be strictly sequential, i.e. 0, 1, 2.
If I call my fix() function, it will iterate through each index of our todos, check if the elements ID matches the expected next ID, if it doesn't, set the elements ID to the current iteration index (i)
void Todo::fix() {
for (int i = 0; i < static_cast<int>(todos.size()); i++) {
if (todos[i].getId() != i) {
todos[i].setId(i);
}
}
}
If we run it after the remove(), our todos will now appear clean again, with the strictly sequential IDs 0, 1, 2:
{Todo[{Item(id=0, desc="Take out the trash.")}, {Item(id=1, desc="Do the laundry.")}, {Item(id=2, desc="Make dinner.")}]}
I got the result I wanted, but here is the question:
Is this the most efficient way to do this task in C++? It seems that if this were a bigger list, it could become resource-intensive and slow. Is there any faster/more efficient way to do this?
Full code including example:
#include <iostream>
#include <vector>
class Item {
private:
int id;
std::string description;
public:
Item() : id(0), description("") {}
Item(int id, std::string description) : id(id), description(description) {}
int getId() const { return id; }
void setId(int value) { id = value; }
std::string getDescription() const { return description; }
void setDescription(std::string value) { description = value; }
};
class Todo {
private:
std::vector<Item> todos;
public:
Todo() = default;
Todo(std::vector<Item> todos) : todos(todos) {};
bool add(std::string description);
bool remove(int id);
bool edit(int id, std::string description);
void list();
void fix();
};
bool Todo::add(std::string description) {
if (description.empty()) {
return false;
}
todos.emplace_back(todos.size(), description);
return true;
}
bool Todo::remove(int id) {
if (id < 0 || id >= static_cast<int>(todos.size())) {
return false;
}
todos.erase(todos.begin() + id);
return true;
}
bool Todo::edit(int id, std::string description) {
if (id < 0 || id >= static_cast<int>(todos.size()) || description.empty()) {
return false;
}
todos[id].setDescription(description);
return true;
}
void Todo::list() {
std::cout << "{Todo[";
for (size_t i = 0; i < todos.size(); ++i) {
const auto& item = todos[i];
std::cout << "{Item(id=" << item.getId() << ", desc=\"" << item.getDescription() << "\")}";
if (i != todos.size() - 1) std::cout << ", ";
}
std::cout << "]}" << std::endl;
}
void Todo::fix() {
for (int i = 0; i < static_cast<int>(todos.size()); i++) {
if (todos[i].getId() != i) {
todos[i].setId(i);
}
}
}
int main() {
Todo todos = Todo();
todos.add("Take out the trash.");
todos.add("Do the laundry.");
todos.add("Read a book.");
todos.add("Make dinner.");
todos.remove(2);
todos.fix();
todos.list();
return 0;
}