# Find the total number of emails in a folder (with all of the subfolders)

The Folder struct definition below is given:

type Folder struct {
ID int
emailCount int
childFolderIDs []int
}


Description of the fields are:

• Id : folder id
• emailCount : number of emails in the folder
• childFolderIDS : if the folder has subfolders, this list contains folder IDS of the subfolders.

I am trying to write a function:

• Function is taking two parameters:
• First parameter is a list of all folders in the email application
  var folders = []Folder{
Folder{1, 30, []int{2, 4}},
Folder{2, 10, []int{3}},
Folder{4, 60, []int{}},
Folder{3, 20, []int{}},
}

• Second parameter is folder id of a folder
• Function will return a total number of emails with ID=Folder Id and all of its children recursively.

For example:

If the second parameter is:

• four, then the function should return 60 (there is no subfolder)

• two, then the function should return 30

My code is below:

      package main

import "fmt"

type Folder struct {
ID int
emailCount int
childFolderIDs []int
}

func InsertIntoMap(data_arr []Folder) map[int]Folder {
var retval = make(map[int]Folder)
var child_folders []int
for _, elem := range data_arr {
child_folders = elem.childFolderIDs
var folder Folder
folder.ID = elem.ID
folder.emailCount = elem.emailCount
folder.childFolderIDs = child_folders
retval[elem.ID] = folder
}

return retval
}

func GetTotalEmailCount(p_map map[int]Folder, folder_id int) int {
total := 0
var child_folders []int
child_folders = p_map[folder_id].childFolderIDs
total = total + p_map[folder_id].emailCount

if len(child_folders) == 0{
}else{
for e := range child_folders {
}
}

}

func main() {

var folders = []Folder{
Folder{1, 30, []int{2, 4}},
Folder{2, 10, []int{3}},
Folder{4, 60, []int{}},
Folder{3, 20, []int{}},
}

var m = InsertIntoMap(folders)

fmt.Println(GetTotalEmailCount(m,1))  // result is 60
fmt.Println(GetTotalEmailCount(m,2))  // result is 30
fmt.Println(GetTotalEmailCount(m,3))  // result is 20
fmt.Println(GetTotalEmailCount(m,4))  // result is 60

}
$$$$

• Welcome to CodeReview. The indentation of your code is inconsistent, and part of your example (Second parameter is:) ended up in a code block. You may want to fix this before reviewing starts. (Code in the question shall stay unchanged conce you got answers. Or serious time passed since asking (- 24 hours?)) – greybeard Aug 3 at 8:36

First, your code doesn't give correnct answers; for (m, 1) the answer should be 120 (30 in ./1, +10 in ./1/2, +20 in ./1/2/3, +60 in ./1/4; you never count more than a single entry in the sub-folder slice).

Also, you should run go fmt (or goimports) on your code (perhaps you did and that was lost when posting). Also, adding a Go Playground link to your original code in posts is helpful.

You mention that you wanted to create a function taking two parameters, the first being []Folder but your code instead converts the slice into a map and uses that as the first argument. Which did you intend? When you have a function or functions that are answering questions based on an argument you could consider making a method. For example, perhaps something like:

type Folders []Folder
// or
type Folders struct {
list []Folder
byID map[int]Folder
}

func (f Folders) EMailCount(folder int) int {
// code that uses f and folder as arguments
}


You use snake_case for some of your identifiers and camelCase for others; idiomatic Go code uses camelCase for all identifiers.

In your InsertIntoMap function:

• When you make the map you know how many entries you expect so you should include that number to make as a size hint (e.g. make(map[int]Folder, len(data))). If the size is large this avoids the having to dynamically re-size the map as you add entries.
• You name the map retval, I'd instead name it based on what contains (a map of folders by ID) rather than it happens to be the return value. I'd call it just m or byID.
• Within the loop you effectively copy the element elem to the variable folder. You can do this with just folder := elem but at that point the entire loop body can just be rewritten as retval[elem.ID] = elem.

In your GetTotalEmailCount function:

• Normally in Go instead of:

var foo []int
foo = something[bar].foo


you'll just see:

foo := something[bar].foo


not only is it shorter but it can make future code changes easier. In the latter if the field foo changes type you don't need to edit the type of the variable foo to match. (By the way, shorter isn't always better. Clarity is more important than conciseness).

• Idiomatic Go code tends avoid indenting code by using early returns (see https://github.com/golang/go/wiki/CodeReviewComments#indent-error-flow). You return if len(…) == 0 so you should just drop the else clause and remove the indent (golint will suggest this). E.g, instead of:

if someCondition {
return something
} else {
// other code
//     possibly with more conditional/loop indenting
}


it would be:

if someCondition {
return something
}

// other code
//     possibly with more conditional/loop indenting

• In this specific case, you don't even need the conditional since for range loops don't do anything on empty/nil slices the following return total line is sufficient.

• It's in this for loop your your bug exists. On the first iteration you stop looping and return the total of this folder and it's first child without iterating to the next child.

return total + GetTotalEmailCount(p_map, child_folders[e])


should be:

total += GetTotalEmailCount(p_map, child_folders[e])


Without changing the basic structure of your code, all the above gives something like this (Go Playground):

package main

import "fmt"

type Folder struct {
ID             int
emailCount     int
childFolderIDs []int
}

func InsertIntoMap(data []Folder) map[int]Folder {
m := make(map[int]Folder, len(data))
for _, e := range data {
m[e.ID] = e
}
return m
}

func GetTotalEmailCount(m map[int]Folder, folder int) int {
children := m[folder].childFolderIDs
total := m[folder].emailCount

for e := range children {
// BUG: doesn't detect infinite loops
total += GetTotalEmailCount(m, children[e])
}

}

func main() {
var folders = []Folder{
Folder{1, 30, []int{2, 4}},
Folder{2, 10, []int{3}},
Folder{4, 60, []int{}},
Folder{3, 20, []int{}},
}

var m = InsertIntoMap(folders)
fmt.Println(GetTotalEmailCount(m, 1)) // result is 120, 30+10+20+60
fmt.Println(GetTotalEmailCount(m, 2)) // result is 30
fmt.Println(GetTotalEmailCount(m, 3)) // result is 20
fmt.Println(GetTotalEmailCount(m, 4)) // result is 60
}
`
• This was my first Go code. Thank you for your valuable comments. – Code Name James Michael Aug 3 at 13:15