A todo cli app in golang

A

So we’re going to build a simple TODO app in go. By this way, I think we can actually practice what we studied so far.

package main

import "fmt"

func main() {
	
}

This is our starting code.

First lets create an empty array.

todos := []string{}

now lets add some elements to it

	todos = append(todos, "wake up")
	todos = append(todos, "code")
	todos = append(todos, "sleep")

Lets print to make sure everything works.

Ah it definitely works. But it looks kinda ugly.

So lets use for loop iterate and print it individually.


	for in, el := range todos {
		fmt.Println(in+1, ")", el)
	}

A lot better!

Now lets delete some values. Say element at index 1

// Remove the element at index i from a.
	i := 1
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

So far it seems to work. But look at our code

package main

import "fmt"

func main() {
	todos := []string{}

	//add some todos
	todos = append(todos, "wake up")
	todos = append(todos, "code")
	todos = append(todos, "sleep")

	for in, el := range todos {
		fmt.Println(in+1, ")", el)
	}

	//deletion
	// Remove the element at index i from a.
	i := 1
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	//print
	for in, el := range todos {
		fmt.Println(in+1, ")", el)
	}
}

It looks messy. Lets use functions!

So after some refractoring, our code should look like this:

package main

import "fmt"

func printTodo(list []string) {
	for in, el := range list {
		fmt.Println(in+1, ")", el)
	}
}

func deleteTodo(todos []string, i int) []string {
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	return todos
}

func main() {
	todos := []string{}

	//add some todos
	todos = append(todos, "wake up")
	todos = append(todos, "code")
	todos = append(todos, "sleep")

	printTodo(todos)

	//deletion
	todos = deleteTodo(todos, 1)

	//print
	printTodo(todos)
}

Now it looks better right?

We appended, deleted from a list, we used for loops and functions and our code seems to work.

But everything is almost hardcoded here. We want users to input data to it.

So how to get input from user? use fmt.Scan()

var i int
fmt.Scan(&i)

This is how you get an integer from user and store it in i

So, I changed the code again

package main

import "fmt"

func printTodo(list []string) {
	for in, el := range list {
		fmt.Println(in+1, ")", el)
	}
}

func deleteTodo(todos []string, i int) []string {
	if len(todos) == 1 {
		fmt.Println("deleted at index", i)
		return []string{}
	}
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	return todos
}

func main() {
	todos := []string{}

	//add some todos
	var todo string
	fmt.Scan(&todo)
	todos = append(todos, todo)

	printTodo(todos)

	//deletion
	var i int
	fmt.Scan(&i)
	todos = deleteTodo(todos, i)

	//print
	printTodo(todos)
}

But theres one problem here. when i use seperated values, this code breaks.

So we’re gonna use buffio instead

func scan() string {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

Now our code looks like this:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func printTodo(list []string) {
	for in, el := range list {
		fmt.Println(in+1, ")", el)
	}
}

func deleteTodo(todos []string, i int) []string {
	if len(todos) == 1 {
		fmt.Println("deleted at index", i)
		return []string{}
	}
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	return todos
}

func scan() string {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func main() {
	todos := []string{}

	//add some todos
	var todo string
	todo = scan()
	todos = append(todos, todo)

	printTodo(todos)

	//deletion
	var i int
	fmt.Scan(&i)
	todos = deleteTodo(todos, i)

	//print
	printTodo(todos)
}

But this looks boring. We can add/delete only one element at a time? And how the user suppose to know what should do? There’s no instructions.

So first lets solve that.

So I added a greet function and used a switch case

func greet() {
	fmt.Println("1) add")
	fmt.Println("2) delete")
	fmt.Println("3) print")
	fmt.Println("4) quit")
}
        var choice int
	fmt.Scan(&choice)

	switch choice {
		case 1:
			fmt.Println("one")
		case 2:
			fmt.Println("two")
		case 3:
			fmt.Println("three")
		case 4:
			fmt.Println("four")
		default:
			fmt.Println("enter a valid number")
	}

Now lets move the logic to its appropriate place

package main

import (
	"bufio"
	"fmt"
	"os"
)

func printTodo(list []string) {
	for in, el := range list {
		fmt.Println(in+1, ")", el)
	}
}

func deleteTodo(todos []string, i int) []string {
	if len(todos) == 1 {
		fmt.Println("deleted at index", i)
		return []string{}
	}
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	return todos
}

func scan() string {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func greet() {
	fmt.Println("1) add")
	fmt.Println("2) delete")
	fmt.Println("3) print")
	fmt.Println("4) quit")
}

func main() {
	todos := []string{}

	greet()

	var choice int
	fmt.Scan(&choice)

	switch choice {
	case 1:
		//add some todos
		var todo string
		todo = scan()
		todos = append(todos, todo)
	case 2:
		//deletion
		var i int
		fmt.Scan(&i)
		todos = deleteTodo(todos, i)
	case 3:
		printTodo(todos)
	case 4:
		break
	default:
		fmt.Println("enter a valid number")
	}

}

Still this code runs only once. We want to run it infinitely. So use a loop

isRun := true

for isRun {

}

I made some decoration in printing statement and now our looks like this

package main

import (
	"bufio"
	"fmt"
	"os"
)

func printTodo(list []string) {
	fmt.Println("--------your todo--------")
	for in, el := range list {
		fmt.Println(in+1, ")", el)
	}
	fmt.Println("-------------------------")
}

func deleteTodo(todos []string, i int) []string {
	if len(todos) == 1 {
		fmt.Println("deleted at index", i)
		return []string{}
	}
	copy(todos[i:], todos[i+1:]) // Shift todos[i+1:] left one index.
	todos[len(todos)-1] = ""     // Erase last element (write zero value).
	todos = todos[:len(todos)-1] // Truncate slice.

	fmt.Println("deleted at index", i)

	return todos
}

func scan() string {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func greet() {
	fmt.Println("1) add")
	fmt.Println("2) delete")
	fmt.Println("3) print")
	fmt.Println("4) quit")
}

func main() {
	todos := []string{}
	isRun := true

	for isRun {

		greet()

		var choice int
		fmt.Print("Enter your choice: ")
		fmt.Scan(&choice)

		switch choice {
		case 1:
			//add some todos
			var todo string
			todo = scan()
			todos = append(todos, todo)
		case 2:
			//deletion
			var i int
			fmt.Scan(&i)
			todos = deleteTodo(todos, i)
		case 3:
			printTodo(todos)
		case 4:
			isRun = false
			break
		default:
			fmt.Println("enter a valid number")
		}

	}

}

And thats the final result.

Not that bad right?

Happy coding!

About the author

vigneshwar

Add comment

Leave a Reply

By vigneshwar

Most common tags

%d bloggers like this: