Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

the question is simple, for a input [apple, banana, orange, apple, banana, apple], the program will count it as a map: {apple : 3, orange: 1, banana: 2}, then sort this map by it's values, get [(apple, 3), (banana, 2), (orange, 1)] below is my go version, I'm a go newbie, could you please review it and lecture me how to polish the code? I add a python version for reference

package main

//input, a url.log file, like "apple\nbanana\norange\napple\nbanana\napple\n"
//output, a output.txt file, should be "apple: 3\nbanana: 2\norange: 1\n"
import (
    "fmt"
    "bufio"
    "os"
    "sort"
    "strings"
    "io"
)

func main() {
    m := make(map[string]int)

    // read file line by line
    filename := "url.log"
    f, err := os.Open(filename)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()
    r := bufio.NewReader(f)
    for line, err := r.ReadString('\n'); err == nil; {
        url := strings.TrimRight(line, "\n")
        m[url] += 1  // build the map
        line, err = r.ReadString('\n')
    }

    if err != nil {
        fmt.Println(err)
        return
    }

    //sort the map by it's value
    vs := NewValSorter(m)
    vs.Sort()

    // output
    f_out, _ := os.Create("output.go.txt") 
    defer f_out.Close()
    var line string
    for i, k := range vs.Keys {
        line = fmt.Sprintf("%s: %d\n", k, vs.Vals[i])
        io.WriteString(f_out, line)
    }

}

type ValSorter struct {
    Keys []string
    Vals []int
}

func NewValSorter(m map[string]int) *ValSorter {
    vs := &ValSorter {
        Keys: make([]string, 0, len(m)),
        Vals: make([]int, 0, len(m)),
    }
    for k, v := range m {
        vs.Keys = append(vs.Keys, k)
        vs.Vals = append(vs.Vals, v)
    }
    return vs
}

func (vs *ValSorter) Sort() {
    sort.Sort(vs)
}

func (vs *ValSorter) Len() int {
    return len(vs.Vals)
}

func (vs *ValSorter) Less(i, j int) bool {
    return vs.Vals[i] > vs.Vals[j]
}

func (vs *ValSorter) Swap(i, j int) {
    vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i]
    vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i]
}

python version:

from collections import Counter

def main():
    count = Counter()
    with open("url.log", "rb") as f:
        for line in f:
            url = line.rstrip()
            count[url] += 1
    with open("output.py.txt", "wb") as f:
        for key, value in count.most_common():
            f.write("%s: %d\n" %(key, value))


if __name__ == "__main__":
    main()
share|improve this question

2 Answers 2

up vote 2 down vote accepted

In Go, I would write:

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "sort"
)

type Key string
type Count int

type Elem struct {
    Key
    Count
}

type Elems []*Elem

func (s Elems) Len() int {
    return len(s)
}

func (s Elems) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

type ByReverseCount struct{ Elems }

func (s ByReverseCount) Less(i, j int) bool {
    return s.Elems[j].Count < s.Elems[i].Count
}

func main() {
    m := make(map[Key]Count)
    fi, err := os.Open("keys.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fi.Close()
    r := bufio.NewReader(fi)
    for {
        line, err := r.ReadString('\n')
        if err != nil {
            if err == io.EOF && len(line) == 0 {
                break
            }
            fmt.Println(err)
            return
        }
        key := line[:len(line)-1]
        m[Key(key)] += 1
    }
    fi.Close()

    e := make(Elems, 0, len(m))
    for k, c := range m {
        e = append(e, &Elem{k, c})
    }
    sort.Sort(ByReverseCount{e})

    fo, err := os.Create("counts.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fo.Close()
    for _, e := range e {
        line := fmt.Sprintf("%s: %d\n", e.Key, e.Count)
        fo.WriteString(line)
    }
}

Input (keys.txt):

apple
banana
orange
apple
banana
apple

Output (counts.txt):

apple: 3
banana: 2
orange: 1
share|improve this answer
    
thanks for your help! –  sunqiang Jun 18 '12 at 14:10

Here's another approach using ioutils, which has some shortcuts for reading/writing files (assuming they are short enough to fit into memory). If you're writing a quick util, this might be easier than dealing with bufio in many cases. I've kept the sorting simple and in one function.

package main

import (
    "fmt"
    "io/ioutil"
    "sort"
    "strings"
)

func main() {
    // Read the input file
    input := "./input.txt"
    input_text, err := ioutil.ReadFile(input)
    if err != nil {
        panic(err)
    }

    // Store line counts
    lines := strings.Split(string(input_text), "\n")
    counts := map[string]int{}
    for _, l := range lines {
        counts[l] += 1
    }

    // Get keys sorted
    keys := []string{}
    for k, _ := range counts {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    // Output sorted counts using ordered keys
    output_text := ""
    for _, k := range keys {
        output_text += fmt.Sprintf("%s : %d\n", k, counts[k])
    }

    // Write results to a file
    output := "./output.txt"
    err = ioutil.WriteFile(output, []byte(output_text), 0644)
    if err != nil {
        panic(err)
    }
    fmt.Print("File written\n", output_text)
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.