Search code examples
csvgoalphabetical

How to alphabetize a csv file using Go?


I'm trying to order a .csv file with a series of names alphabetically using Go, based off of the last name, which is in the first column. I've searched all over, but I can't seem to find a way to do it. Is there a way to do this, while holding onto the other values in the same row? I have three .csv files with the same names, but I have to shuffle them in order to complete my task (a random table seating algorithm). I want to be able to put them back into a defined alphabetical order so that I can be sure that people don't sit with each other consecutively.

Thanks in advance. :)

EDIT: Might be worth showing my function I use to shuffle it:

func Shuffle(slice []Person) []Person {
r := rand.New(rand.NewSource(time.Now().Unix()))
ret := make([]Person, len(slice))
n := len(slice)
for i := 0; i < n; i++ {
    randIndex := r.Intn(len(slice))
    ret[i] = slice[randIndex]
    slice = append(slice[:randIndex], slice[randIndex+1:]...)
}
return ret

The Person[] slice is just a struct that holds a first and last name.


Solution

  • Go's sort package comes with a great example. See an amended implementation that should do what you're asking about.

    package main
    
    import (
        "encoding/csv"
        "fmt"
        "io"
        "log"
        "sort"
        "strings"
    )
    
    // Unsorted sample data
    var unsorted = `Balaam,Wileen,Saint Louis
    Meachan,Lothaire,Lefengzhen
    Scoggin,Ivonne,Pag
    Hawarden,Audrye,Leiria
    Claypool,Biddy,Maiorca
    Stanford,Douglas,Báguanos
    Petriello,Yvor,Obryte
    Hatter,Margette,Luoping
    Pepall,Linzy,Hucun
    Carter,Kit,Parungjawa
    `
    
    type Person struct {
        Lastname  string
        Firstname string
        City      string
    }
    
    // Create a new Person record from a given string slice
    func NewPerson(fields []string) (p Person, err error) {
        if len(fields) < 3 {
            return p, fmt.Errorf("not enough data for Person")
        }
        p.Lastname = fields[0]
        p.Firstname = fields[1]
        p.City = fields[2]
        return
    }
    
    // ByLastname implements sort.Interface for []Person based on the Lastname field.
    type ByLastname []Person
    
    func (a ByLastname) Len() int           { return len(a) }
    func (a ByLastname) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    func (a ByLastname) Less(i, j int) bool { return a[i].Lastname < a[j].Lastname }
    
    func main() {
        // Open unsorted CSV from string
        r := csv.NewReader(strings.NewReader(unsorted))
    
        var people []Person
    
        for {
            // Read CSV line by line
            record, err := r.Read()
            if err == io.EOF {
                break
            }
            if err != nil {
                log.Fatal(err)
            }
    
            // Create Person from line in CSV
            person, err := NewPerson(record)
            if err != nil {
                continue
            }
            people = append(people, person)
        }
    
        // Sort CSV by Lastname
        sort.Sort(ByLastname(people))
    
        // Print to stdout
        for _, p := range people {
            fmt.Printf("%s %s from %s\n", p.Lastname, p.Firstname, p.City)
        }
    
        // Here you would write your CSV
    }