Golang中国

本站的 Go 下载 一直有个让站长很不爽的地方,就是版本的排序有问题。加上太忙也静不下心来搞。

顺带活跃下气氛,就搞一次悬赏活动吧。

要求:

输入一个版本字符串 slice,返回排序的 slice。

func VersionSort(versions []string) []string {

}

下面是例子:

[]string {"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}

要求返回:

[]string {"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}

奖金模式

奖品为200元购书金额,请获奖者将图书目录列给我,我直接从网上购买快递。

奖金分配方式

独享,选取最早提交且满足要求的方案。如果有晚提交的用户的方案也满足要求,且实现优雅,会再提供100元购物金额奖励。

代码版权

最终满足方案的代码将用于 Golang 中国的代码中。

方案提交方式

在该主题回复即可,注意格式化代码。


jimmykuu 于 2016-01-14 11:22 修改
46 回复
jihua0a
#1 jihua0a • 2016-01-14 11:39

为何不给每个版本附加上发布时间,以发布时间排序不是最简单吗?

jimmykuu
#2 jimmykuu • 2016-01-14 11:45

@jihua0a ,我原先是用配置文件做的,每次都要维护一个配置文件,工作量比较大。

现在干脆直接用最简单的方式处理,用文件夹按版本存储,而且这个排序方法也可以通用。

版本排序确实是个需求,Python 就有个 natsort

stevewang
#3 stevewang • 2016-01-14 14:15
package main

import (
    "fmt"
    "sort"
    "strings"
)

type VersionList []string

func (v VersionList) Len() int {
    return len(v)
}

func (v VersionList) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}

func (v VersionList) Less(i, j int) bool {
    vi := v.parse(v[i])
    vj := v.parse(v[j])
    for index := 0; index < 3; index++ {
        if vi[index] != vj[index] {
            return vi[index] < vj[index]
        }
    }
    return true
}

func (v VersionList) parse(version string) []string {
    fields := strings.Split(version, ".")
    for i := len(fields); i < 3; i++ {
        fields = append(fields, "")
    }
    middle := []byte(fields[1])
    digital := true
    for i := 0; i < len(middle); i++ {
        switch {
        case middle[i] < '0' || middle[i] > '9':
            digital = false
        case middle[i] >= 'A' && middle[i] <= 'Z':
            middle[i] = 'a' + middle[i] - 'A'
        }
    }
    if digital {
        fields[1] = string(middle) + "z"
    }
    return fields[:3]
}

func VersionSort(versions []string) []string {
    sort.Sort(VersionList(versions))
    return versions
}

func main() {
    versions := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    VersionSort(versions)
    fmt.Println(versions)
}
liutp
#4 liutp • 2016-01-14 14:48
package main

import (
    "sort"
    "strconv"
    "strings"
    "errors"
    "fmt"
)

type TheVersion struct {
    Version uint64
    Flag    uint64
    Raw     string
}

func (ve *TheVersion) Great(other *TheVersion) bool {
    if ve.Version > other.Version {
        return true
    } else if ve.Version == other.Version && ve.Flag > other.Flag {
        return true
    }
    return false
}
func (ve *TheVersion) Less(other *TheVersion) bool {
    if ve.Version < other.Version {
        return true
    } else if ve.Version == other.Version && ve.Flag < other.Flag {
        return true
    }
    return false
}
func (ve *TheVersion) String() string {
    return ve.Raw
}
func (ve *TheVersion) Parse(str string) error {
    ve.Raw = str
    split := make([]string, 0)
    tmpStr := ""
    isNum := true
    for _, c := range str {
        if c == '.' {
            split = append(split, tmpStr)
            tmpStr = ""
        } else if c >= '0' && c <= '9' {
            if isNum == false {
                isNum = true
                split = append(split, tmpStr)
                tmpStr = ""
            }
            tmpStr = tmpStr + string(c)
        } else {
            if isNum == true {
                isNum = false
                split = append(split, tmpStr)
                tmpStr = ""
            }
            tmpStr = tmpStr + string(c)
        }
    }
    split = append(split, tmpStr)
    //fmt.Println(split)

    if len(split) >= 2 {
        v0, err := strconv.ParseUint(split[0], 10, 10)
        if err != nil {
            return err
        }
        v1, err := strconv.ParseUint(split[1], 10, 10)
        if err != nil {
            return err
        }
        ve.Version = v0*1000000 + v1
    } else if len(split) == 1 {
        v0, _ := strconv.ParseUint(split[0], 10, 10)
        ve.Version = v0 * 1000000
    } else {
        return errors.New("ERROR")
    }

    var err error
    ve.Flag = 900000
    if len(split) == 4 {
        if strings.ToLower(split[2]) == "alpha" {
            ve.Flag, err = strconv.ParseUint(split[3], 10, 10)
            ve.Flag = 100000 + ve.Flag
        } else if strings.ToLower(split[2]) == "beta" {
            ve.Flag, err = strconv.ParseUint(split[3], 10, 10)
            ve.Flag = 200000 + ve.Flag
        } else if strings.ToLower(split[2]) == "rc" {
            ve.Flag, err = strconv.ParseUint(split[3], 10, 10)
            ve.Flag = 800000 + ve.Flag
        } else {
            err = errors.New("Unknow")
        }
        if err != nil {
            return err
        }
    }
    if len(split) == 3 {
        ve.Flag, err = strconv.ParseUint(split[2], 10, 10)
        if err != nil {
            return err
        }
        ve.Flag = 900000 + ve.Flag
    }
    return nil
}

type MySortList []*TheVersion

func (list MySortList) Len() int {
    return len(list)
}
func (list MySortList) Less(i, j int) bool { //从大到小
    u1 := list[i]
    u2 := list[j]
    return u1.Less(u2)
}
func (list MySortList) Swap(i, j int) {
    var temp *TheVersion = list[i]
    list[i] = list[j]
    list[j] = temp
}

func MySort(in []string) []string {
    ver := make([]*TheVersion, 0)
    for _, s := range in {
        v := &TheVersion{}
        err := v.Parse(s)
        if err != nil {
            fmt.Println(err.Error())
            return in
        }
        ver = append(ver, v)
    }
    sort.Sort(MySortList(ver))
    for i, v := range ver {
        in[i] = v.Raw
    }
    return in
}

func MyTest(in []string, out []string) bool {
    if len(in) != len(out) {
        return false
    }
    for i, _ := range in {
        if in[i] != out[i] {
            return false
        }
    }
    return true
}

func main() {
    in := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    out := []string{"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}
    fmt.Println(MySort(in))
    fmt.Println(MyTest(in, out))
}
leedstyh
#5 leedstyh • 2016-01-14 15:30

我也来个,不是程序猿,太复杂的做法不会。

package main

import (
    "log"
    "sort"
    "strconv"
    "strings"
)

//基本思路:
//将版本号,统一格式化成x.yy.zzz的形式,然后转化成xyyzzz的int

var mp = map[string]string{
    "alpha": ".01",
    "beta":  ".02",
    "rc":    ".03",
    // .040
}

func main() {
    slc := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    log.Println(VersionSort(slc))
}

func VersionSort(versions []string) []string {
    index := []int{}
    mp := map[int]string{}
    for _, item := range versions {
        n := convert(sub(item))
        index = append(index, n)
        mp[n] = item
    }

    sort.Ints(index)

    ret := []string{}
    for _, n := range index {
        ret = append(ret, mp[n])
    }
    return ret
}

func sub(s string) string {
    for k, v := range mp {
        if strings.Contains(s, k) {
            s = strings.Replace(s, k, v, -1)
            break
        }
    }
    return s
}

// x.yy.zzz -> xyyzzz which is an int
func convert(s string) int {
    slc := strings.Split(s, ".")

    if len(slc[1]) == 1 { //兼容以后次级版本号到两位数
        slc[1] = "0" + slc[1]
    }

    if len(slc) == 3 && len(slc[2]) == 1 {
        slc[2] = slc[2] + "00"
    }

    if len(slc) == 2 {
        //无第三级版本号(如1.2),将之设置为.040
        slc = append(slc, "040")
    }

    s = strings.Replace(strings.Join(slc, "."), ".", "", -1)

    i, _ := strconv.Atoi(s)
    return i
}
fantasyxky
#6 fantasyxky • 2016-01-14 16:13

Idea:

  • Version number: x.y.z where each digit could be [0-9]+(.*)
  • alpha<beta<rc(release candidate)<release thus 1.3rc < 1.3
  • Use Golang sort interface which makes O(n*log(n)) calls to data.Less and data.Swap

Demo can be found here.

package main

import (
    "fmt"
    "regexp"
    "sort"
    "strings"
    "strconv"
)

var data = []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}

type VersionSlice []string

var reg, _ = regexp.Compile("^([0-9]+)(.*)")

func (p VersionSlice) Len() int {
    return len(p)
}
func (p VersionSlice) Less(i, j int) bool {
    ver1 := strings.Split(data[i], ".")
    ver2 := strings.Split(data[j], ".")
    for i, v1 := range ver1 {
        if i == len(ver2) { // if ver1 has more minor version than ver2, ver1 is after ver 2
            return false
        }
        v2 := ver2[i]
        if v1 != v2 { // if ver1's minor version not equal to ver2. compare. alpha < beta < rc < (r)
            v1Match := reg.FindStringSubmatch(v1)[1:]
            v2Match := reg.FindStringSubmatch(v2)[1:]
            if v1Match[0] != v2Match[0] {
                num1,_:= strconv.Atoi(v1Match[0])    
                num2,_:= strconv.Atoi(v2Match[0])    
                return num1<num2
            }else{ //compare alpha beta rc etc
                if (len(v1Match[1])==0){
                    return false
                }else if (len(v2Match[1])==0){
                    return true
                }else{
                    return v1<v2
                }
            }
        }
    }

    return true
    //ver2:=data[j]

}
func (p VersionSlice) Swap(i, j int) {
    tmp := p[i]
    p[i] = p[j]
    p[j] = tmp
}
func main() {
    res:=VersionSort(data)
    fmt.Println(res)
}

func VersionSort(versions []string) []string {
    versionSlice := VersionSlice(versions)
    sort.Sort(versionSlice)
    return []string(versionSlice) 
}

Output is

[1.0.2 1.0.3 1.1 1.1.1 1.1.2 1.2rc1 1.2rc2 1.2rc3 1.2rc4 1.2rc5 1.2 1.2.1 1.2.2 1.3beta1 1.3beta2 1.3rc1 1.3rc2 1.3 1.3.1 1.3.2 1.3.3 1.4beta1 1.4rc1 1.4rc2 1.4 1.4.1 1.4.2 1.5alpha1 1.5beta1 1.5beta2 1.5rc1 1.5 1.5.1 1.5.2 1.6beta1]
iamcc
#7 iamcc • 2016-01-14 17:42
/*
* @Author: CC
* @Date:   2016-01-14 16:41:12
* @Last Modified by:   CC
* @Last Modified time: 2016-01-14 17:39:54
 */

package main

import (
    "fmt"
    "sort"
    "strings"
)

var (
    versions    = []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    versionsMap = make(map[string]string)
)

func VersionSort(versions []string) []string {
    for i, v := range versions {
        ov := v
        v = strings.Replace(v, "alpha", ".0000", -1)
        v = strings.Replace(v, "beta", ".0001", -1)
        v = strings.Replace(v, "rc", ".0002", -1)
        vv := strings.Split(v, ".")
        vv[0] = strings.Repeat("0", 2-len(vv[0])) + vv[0]
        vv[1] = strings.Repeat("0", 2-len(vv[1])) + vv[1]
        if len(vv) == 2 {
            vv[1] += "0003"
        }

        v = strings.Join(vv, "")
        versions[i] = v
        versionsMap[v] = ov
    }

    sort.Strings(versions)

    for i, v := range versions {
        versions[i] = versionsMap[v]
    }

    return versions
}

func main() {
    fmt.Println(VersionSort(versions))
}
golang_learn
#8 golang_learn • 2016-01-14 17:42
func VersionSort(versions []string) []string {
    for i := 0; i < len(versions); i++ {
        temp := strings.Replace(versions[i], ".", "z", -1)
        for _, alpha := range []string{"alpha", "beta", "rc"} {
            if !strings.Contains(temp, alpha) {
                temp += "y"
                break
            }
        }
        versions[i] = temp
    }

    sort.Strings(versions)

    for i := 0; i < len(versions); i++ {
        temp := strings.Replace(versions[i], "z", ".", -1)
        versions[i] = strings.Replace(temp, "y", "", 1)
    }
    return versions
}
zxh
#9 zxh • 2016-01-14 18:45
package main

import "sort"
import "strings"

var unsorted = []string {"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}

func main() {
    sorted := VersionSort(unsorted)
    for _, v := range sorted {
        println(v)
    }
}

func VersionSort(versions []string) []string {
    s2 := make([]string, len(versions))
    for i, v := range versions {
        s2[i] = strings.Replace(v, ".", "~", -1) + "~"
    }
    sort.Strings(s2)
    for i, v := range s2 {
        s2[i] = strings.Replace(v[:len(v)-1], "~", ".", -1)
    }
    return s2
}
Comdex
#10 Comdex • 2016-01-14 18:57

我也来一个!

package main

import (
    "fmt"
    "sort"
    "strings"
)

type VersionList []string

func (v VersionList) Len() int {
    return len(v)
}
func (v VersionList) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}

func (v VersionList) Less(i, j int) bool {
    if v.containLetter(v[i]) && v.containLetter(v[j]) {
        return v[i] < v[j]
    }
    if !v.containLetter(v[i]) && !v.containLetter(v[j]) {
        return v[i] < v[j]
    }

    fields := strings.Split(v[i], ".")
    fields2 := strings.Split(v[j], ".")

    if fields[0] < fields2[0] {
        return true
    } else if fields[0] > fields2[0] {
        return false
    } else {
        if fields[1][0] < fields2[1][0] {
            return true
        } else if fields[1][0] == fields2[1][0] {
            if v.containLetter(v[i]) {
                return true
            }else{
                return false
            }
        } else if fields[1][0] > fields2[1][0] {
            return false
        }
    }    

    return v[i] < v[j]
}

func (v VersionList) containLetter(version string) bool {
    if strings.Contains(version, "rc") || strings.Contains(version, "beta") || strings.Contains(version, "alpha") {
        return true
    }
    return false
}

func VersionSort(versions []string) []string {
    sort.Sort(VersionList(versions))
    return versions
}

func main() {
    versions := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    VersionSort(versions)
    fmt.Println(versions)
}
Comdex
#11 Comdex • 2016-01-14 19:38

九楼的这样也行

func VersionSort(versions []string) []string {
    s2 := make([]string, len(versions))
    for i, v := range versions {
        s2[i] = strings.Replace(v, ".", "|", -1) + "|"
        println(s2[i])
    }
    sort.Strings(s2)
    for i, v := range s2 {
        s2[i] = strings.Replace(v[:len(v)-1], "|", ".", -1)
    }
    return s2
}
Comdex
#12 Comdex • 2016-01-14 21:36

做了一个可能并不准确的测试,在32位机器,win7系统下跑楼上的代码测试运行所需要的时间

测试的主要代码为:

func main() {
    versions := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1",
        "1.7", "1.7.1", "1.7rc2", "1.7beta3", "1.8.1", "1.8.0"}
    for i := 0; i < 10000; i++ {
        //增加多一点数据
        versions = append(versions, "1.9beta3")
    }
    start := time.Now()
    //VersionSort分别调用1到9楼的代码
    VersionSort(versions)
    end := time.Now()
    fmt.Println("运行时间: ", end.Sub(start))
}

得出的结果如下:
1楼:673.0385ms
4楼:31.0017ms
5楼:26.0015ms
6楼:451.0258ms
7楼:22.0012ms
8楼:16.001ms
9楼:10.0006ms
10楼:7.0004ms

哈哈,测试结果仅供参考

snake117
#13 snake117 • 2016-01-14 22:02
package main

import (
    "fmt"
    "sort"
    "strconv"
    "strings"
)

// 用于记录版本信息的结构体
type Version struct {
    num []byte
    typ byte
    tpn byte
}

// 定义该类型并实现sort.Interface接口
type List []Version

var list = []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}

func main() {
    fmt.Println(list)
    Sort(list)
    fmt.Println(list)
}

// 排序函数,参照sort.Sort
func Sort(s []string) {
    medi := make(List, len(s))
    for i, s := range s {
        medi[i].Set(s)
    }
    sort.Sort(medi)
    for i, _ := range medi {
        s[i] = medi[i].String()
    }
}

// 根据版本字符串生成结构体
func (this *Version) Set(s string) {
    i := strings.IndexAny(s, "abr")
    if i < 0 {
        this.typ = 's'
        this.tpn = 0
    } else {
        this.typ = s[i]
        switch this.typ {
        case 'a':
            u, _ := strconv.Atoi(s[i+5:])
            this.tpn = byte(u)
        case 'b':
            u, _ := strconv.Atoi(s[i+4:])
            this.tpn = byte(u)
        case 'r':
            u, _ := strconv.Atoi(s[i+2:])
            this.tpn = byte(u)
        }
        s = s[:i]
    }
    p := strings.Split(s, ".")
    this.num = make([]byte, len(p))
    for i, s = range p {
        u, _ := strconv.Atoi(s)
        this.num[i] = byte(u)
    }
}

// 将结构体中的信息重构为版本字符串
func (this *Version) String() string {
    p := make([]string, len(this.num))
    for i, n := range this.num {
        p[i] = strconv.Itoa(int(n))
    }
    s := strings.Join(p, ".")
    switch this.typ {
    case 'a':
        s += "alpha" + strconv.Itoa(int(this.tpn))
    case 'b':
        s += "beta" + strconv.Itoa(int(this.tpn))
    case 'r':
        s += "rc" + strconv.Itoa(int(this.tpn))
    }
    return s
}

// 结构体的比较
func (this *Version) Less(that *Version) bool {
    i, j := len(this.num), len(that.num)
    for k := 0; k < i && k < j; k++ {
        if this.num[k] < that.num[k] {
            return true
        }
        if this.num[k] > that.num[k] {
            return false
        }
    }
    if i < j {
        return true
    }
    if i > j {
        return false
    }
    if this.typ < that.typ {
        return true
    }
    if this.typ > that.typ {
        return false
    }
    if this.tpn < that.tpn {
        return true
    }
    return false
}

func (this List) Len() int {
    return len(this)
}

func (this List) Less(i, j int) bool {
    return this[i].Less(&this[j])
}

func (this List) Swap(i, j int) {
    this[i], this[j] = this[j], this[i]
}
baiyuxiong
#14 baiyuxiong • 2016-01-14 23:16

推荐9楼,简单清晰。
直接用sort.Strings排序会因为数字比字母小,导致部分排序有问题,用~去掉这个影响。

snake117
#15 snake117 • 2016-01-15 00:23

@baiyuxiong

不行的,现在是版本号里的数字都是单个的,所以可以;如果是多个数字的话(没人保证go不会出现这种版本号,也没人说这个函数只用在go的版本号上)就会出问题了。

jimmykuu
#16 jimmykuu • 2016-01-15 09:54

最终我会做些边界检查,比如出现 10.0.1这种版本号,目前对于我这边的需求来说,这些方案都够用了,但是通用的还要经过测试。

golang_learn
#17 golang_learn • 2016-01-15 10:10
func IsAlpha(ch byte) bool {
    return ch >= 'a' && ch <= 'z'
}

type VersionSlice []string

func (vs VersionSlice) Len() int {
    return len(vs)
}

func (vs VersionSlice) Swap(i, j int) {
    vs[i], vs[j] = vs[j], vs[i]
}

func (vs VersionSlice) Less(i, j int) bool {
    main_ver := 3
    for n := 0; n < main_ver; n++ {
        switch {
        case vs[i][n] < vs[j][n]:
            return true
        case vs[i][n] > vs[j][n]:
            return false
        }
    }

    // 次版本比较,(x.alphaN<x.betaN<x.rcN<x<x.N)
    i_len, j_len := len(vs[i]), len(vs[j])
    if i_len == main_ver {
        return !IsAlpha(vs[j][main_ver])
    } else if j_len == main_ver {
        return IsAlpha(vs[i][main_ver])
    } else {
        chi, chj := vs[i][main_ver], vs[j][main_ver]
        if IsAlpha(chi) {
            if IsAlpha(chj) {
                return vs[i][main_ver:i_len] < vs[j][main_ver:j_len]
            } else {
                return true
            }
        } else {
            if IsAlpha(chj) {
                return false
            } else {
                return vs[i][main_ver:i_len] < vs[j][main_ver:j_len]
            }
        }
    }
}

func VersionSort(versions []string) []string {
    sort.Sort(VersionSlice(versions))
    return versions
}
xiaoxiaoyijan
#18 xiaoxiaoyijan • 2016-01-15 12:06
package main

import (
    "fmt"
    "regexp"
    "sort"
    "strconv"
)

type Version struct {
    Major    int
    Minor    int
    Build    string
    BuildNum int
    Revision int
    Raw      string
    MagicNum uint64
}

var buildOrder = map[string]int{
    "alpha": 1,
    "beta":  2,
    "rc":    3,
}

func (v *Version) CalcMagicNum() {
    var result uint64

    if v.Revision > 0 {
        result += (uint64)(v.Revision)
    }

    if v.BuildNum > 0 {
        result += 100 * (uint64)(v.BuildNum)
    }

    if v.Build != "" {
        result += 100 * 100 * (uint64)(buildOrder[v.Build])
    } else {
        result += uint64(100 * 100 * 9) // no build
    }
    result += 100 * 100 * 100 * (uint64)(v.Minor)
    result += 100 * 100 * 100 * 100 * (uint64)(v.Minor)

    v.MagicNum = result
}

type Versions []Version

func (p Versions) Len() int {
    return len(p)
}
func (p Versions) Less(i, j int) bool {
    return p[i].MagicNum < p[j].MagicNum
}
func (p Versions) Swap(i, j int) {
    tmp := p[i]
    p[i] = p[j]
    p[j] = tmp
}

func VersionSort(versions []string) []string {
    versionExp := regexp.MustCompile(`^(?P<major>\d+)\.(?P<minor>\d+)(?P<build>alpha|beta|rc)?(?P<build_num>\d+)?\.?(?P<revision>\d+)?$`)

    var match_result []string
    version_list := make(Versions, 1)
    for _, v := range versions {
        match_result = versionExp.FindStringSubmatch(v)
        if len(match_result) != 6 {
            fmt.Println("Invalid version: " + v)
            continue
        }

        version := Version{
            Raw:      match_result[0],
            Major:    -1,
            Minor:    -1,
            Build:    match_result[3],
            BuildNum: -1,
            Revision: -1,
        }
        if match_result[1] != "" {
            version.Major, _ = strconv.Atoi(match_result[1])
        }
        if match_result[2] != "" {
            version.Minor, _ = strconv.Atoi(match_result[2])
        }
        if match_result[4] != "" {
            version.BuildNum, _ = strconv.Atoi(match_result[4])
        }
        if match_result[5] != "" {
            version.Revision, _ = strconv.Atoi(match_result[5])
        }

        if version.Major < 0 || version.Minor < 0 || (version.Build != "" && version.BuildNum < 0) {
            fmt.Println("Invalid version: " + version.Raw)
            continue
        }

        version.CalcMagicNum()
        version_list = append(version_list, version)
    }

    sort.Sort(version_list)

    result := []string{}
    for _, v := range version_list {
        result = append(result, v.Raw)
    }

    return result
}

func main() {

    versions := []string{
        "1.6beta1",
        "1.5rc1",
        "1.5beta2",
        "1.5beta1",
        "1.5.1",
        "1.5",
        "1.4rc2",
        "1.4rc1",
        "1.4beta1",
        "1.4.2",
        "1.4.1",
        "1.4",
        "1.3rc2",
        "1.3rc1",
        "1.3beta2",
        "1.3beta1",
        "1.3.3",
        "1.3.2",
        "1.3.1",
        "1.3",
        "1.2rc5",
        "1.2rc4",
        "1.2rc3",
        "1.2rc2",
        "1.2rc1",
        "1.2.2",
        "1.2.1",
        "1.2",
        "1.1.2",
        "1.1.1",
        "1.1",
        "1.0.3",
        "1.0.2",
        "1.5.2",
        "1.5alpha1",
    }
    fmt.Printf("input versions: %v\n", versions)
    result := VersionSort(versions)
    fmt.Printf("sorted versions: %v\n", result)
}
gleport
#19 gleport • 2016-01-15 13:34

有个 go-version 包:

package main

import (
    "fmt"

    "github.com/mcuadros/go-version"
)

func MyTest(in []string, out []string) bool {
    if len(in) != len(out) {
        return false
    }
    for i, _ := range in {
        if in[i] != out[i] {
            return false
        }
    }
    return true
}

func main() {
    in := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    out := []string{"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}
    version.Sort(in)
    fmt.Println(MyTest(in, out))
}
JessonChan
#20 JessonChan • 2016-01-15 15:31

9楼明显更加geek,但是不够优雅,9楼的代码也可以按这个思路来改下。

package main

import (
    "fmt"
    "sort"
    "strconv"
    "strings"
)

type VersionString struct {
    sort.StringSlice
}

var replacer = strings.NewReplacer("rc", ".00", "beta", ".000", "alpha", ".0000")

func (p VersionString) Less(i, j int) bool {
    atof := func(s string) float64 {
        s = replacer.Replace(s)
        if strings.Count(s, ".") == 1 {
            s += ".01"
        }
        i := strings.Index(s, ".")
        f, _ := strconv.ParseFloat(s[:i]+s[i+1:], 64)
        return f
    }
    return atof(p.StringSlice[i]) < atof(p.StringSlice[j])
}

func VersionSort(versions []string) []string {
    sort.Sort(VersionString{versions})
    return versions
}

func main() {
    ss := []string{"1.6beta1", "1.5rc1", "1.5beta1", "1.5beta10", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    //fmt.Println(VersionSort(ss))
    //or like this
    sort.Sort(VersionString{ss})
    fmt.Println(ss)
}
dicom
#21 dicom • 2016-01-15 17:35
package main

import (
    "fmt"
    "regexp"
    "sort"
    "strconv"
)

const (
    ALPHA_BUILD   = 0
    BETA_BUILD    = 1
    RC_BUILD      = 2
    RELEASE_BUILD = 3
)

var versionPattern = regexp.MustCompile("^\\s*(\\d+)\\.(\\d+)(alpha|beta|rc|\\.)?(\\d+)?\\s*$")

func string2BuildType(str string) int {
    switch str {
    case ".":
        return RELEASE_BUILD
    case "alpha":
        return ALPHA_BUILD
    case "beta":
        return BETA_BUILD
    case "rc":
        return RC_BUILD
    }

    return RELEASE_BUILD
}

type VersionList []string

func (vl VersionList) Len() int {
    return len(vl)
}

func (vl VersionList) Less(i, j int) bool {
    ni := versionString2Number(vl[i])
    nj := versionString2Number(vl[j])
    return ni < nj
}

func (vl VersionList) Swap(i, j int) {
    vl[i], vl[j] = vl[j], vl[i]
}

var cache = make(map[string]uint64)

func versionString2Number(versionStr string) (result uint64) {
    if value, found := cache[versionStr]; found {
        result = value
        return result
    }

    if versions := versionPattern.FindAllStringSubmatch(versionStr, 1); versions != nil {
        version := versions[0]
        major, _ := strconv.Atoi(version[1])
        minor, _ := strconv.Atoi(version[2])

        buildType := RELEASE_BUILD
        if version[3] != "" {
            buildType = string2BuildType(version[3])
        }

        build := 0
        if version[4] != "" {
            build, _ = strconv.Atoi(version[4])
        }

        result = uint64(((major & 0xFFFF) << 16) + (minor & 0xFFFF))
        result = result << 32
        result += uint64(((buildType & 0xFFFF) << 16) + (build & 0xFFFF))
    } else {
        result = 0
    }
    cache[versionStr] = result
    return result
}

func VersionSort(versions []string) []string {
    sort.Sort(VersionList(versions))
    return versions
}

func main() {
    versions := []string{
        "1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1",
        "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1",
        "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2",
        "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1",
    }
    VersionSort(versions)
    fmt.Println(versions)
}
beego
#22 beego • 2016-01-16 21:09

这个帖子最热 O(∩_∩)O哈!

dududu
#23 dududu • 2016-01-17 11:09

这样的活动请多举办一些!

damao
#24 damao • 2016-01-18 07:48

感觉关键还是看版本号的命名规则,比如符合semver的就有现成的semver,不过重在参与我也来个,跟楼上不太一样的是考虑多一点点,比如单个数字“28”(LiteIDE),可以任意添加字符版本如“preview”,在我自己的测试中添加有“10alpha1”,“1.0preview”,“2”,“28”等字串。

package main

import (
    "errors"
    "sort"
    "strconv"
    "strings"
)

var (
    sortHints = []string{
        "preview", ".-4.0",
        "alpha", ".-3.0",
        "beta", ".-2.0",
        "rc", ".-1.0",
    }

    errVersion = errors.New("无法识别的版本")

    replacer = strings.NewReplacer(sortHints...)
)

const (
    Major = iota
    Minor
    Patch
    Pre
    PreVer
    VerFieldCount
)

type version struct {
    versNo    [VerFieldCount]int16
    verString string
}

func (v *version) parse(s string) error {
    parts := strings.Split(replacer.Replace(s), ".")
    field := Major
    for i := 0; i < len(parts); i++ {
        n, err := strconv.ParseInt(parts[i], 10, 16)
        if err != nil {
            return err
        }
        if n < 0 {
            if field == Major || field == PreVer {
                return errVersion
            }
            field = Pre
        }
        v.versNo[field] = int16(n)
        field++
        if field > VerFieldCount {
            return errVersion
        }
    }
    v.verString = s
    return nil
}

type versions []*version

func newVersoins(versions []string) (versions, error) {
    l := len(versions)
    mem := make([]version, l)
    r := make([]*version, l)
    for i := 0; i < l; i++ {
        err := mem[i].parse(versions[i])
        if err != nil {
            return nil, errors.New(versions[i] + ": " + err.Error())
        }
        r[i] = &mem[i]
    }
    return r, nil
}

func (vs versions) Len() int {
    return len(vs)
}

func (vs versions) Less(i, j int) bool {
    a := vs[i].versNo
    b := vs[j].versNo
    for i := Major; i < VerFieldCount; i++ {
        if a[i] != b[i] {
            return a[i] < b[i]
        }
    }
    return false
}

func (vs versions) Swap(i, j int) {
    vs[i], vs[j] = vs[j], vs[i]
}

func (vs versions) getVersions() []string {
    r := make([]string, vs.Len())
    for i, v := range vs {
        r[i] = v.verString
    }
    return r
}

//调用本函数排序
func VersionSort(versions []string) ([]string, error) {
    vs, err := newVersoins(versions)
    if err != nil {
        return nil, err
    }
    sort.Sort(vs)
    return vs.getVersions(), nil
}
peterrk
#25 peterrk • 2016-01-18 10:21
var priority [256]byte

func init() {
    var keys = []uint{'a', 'b', 'r', 0, '.',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
    for i := 0; i < len(keys); i++ {
        priority[keys[i]] = byte(i + 1)
    }
}
func VersionSort(versions []string) {
    insertSort(versions, func(a, b string) bool {
        var i = 0
        for i < len(a) && i < len(b) &&
            a[i] == b[i] {
            i++
        }
        var va, vb = byte(0), byte(0)
        if i < len(a) {
            va = a[i]
        }
        if i < len(b) {
            vb = b[i]
        }
        return priority[va] < priority[vb]
    })
}

func insertSort(list []string, less func(a, b string) bool) {
    for i := 1; i < len(list); i++ {
        var key = list[i]
        var a, b = 0, i
        for a < b {
            var m = (a + b) / 2
            if less(key, list[m]) {
                b = m
            } else {
                a = m + 1
            }
        }
        for j := i; j > a; j-- {
            list[j] = list[j-1]
        }
        list[a] = key
    }
}

好吧,我实际上是个C党…

void VersionSort(std::vector<const char*>& list)
{
    static const struct _ {
        uint8_t v[256];
        uint8_t operator[](uint8_t ch) const { return v[ch]; }
        _(void) {
            static const uint8_t keys[] = { 'a', 'b', 'r', 0, '.',
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            for (unsigned i = 0; i < sizeof(keys); i++) {
                v[keys[i]] = i + 1;
            }
        }
    } priority;
    std::sort(list.begin(), list.end(),
        [](const char* a, const char* b)->bool {
            unsigned i = 0;
            while (a[i] == b[i] && a[i]) i++;
            return priority[a[i]] < priority[b[i]];
        });
}
miraclesu
#26 miraclesu • 2016-01-19 22:51

我也撸了一个 natsort,测试了上面一些同学的代码,有些同学好像没有考虑字符串后面的数字大小这种情况:

versions := []string{"1.6beta10", "1.6", "1.6beta2"}
// [1.6beta2 1.6beta10 1.6]
fmt.Printf("%+v\n", natsort.Sort(versions))
wangzhikk
#27 wangzhikk • 2016-01-20 13:35

来个高端大气上档次的, 比10楼更快

package main
import (
    "fmt"
    "sort"
    "strings"
    "time"
)
func main() {
    inStr:=[]string {"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    begin:=time.Now()
    inStr=sortTem(inStr)
    end:=time.Now();
    fmt.Println(end.Sub(begin))
    fmt.Println(inStr)
}
func sortTem(list []string )[]string{
    length:=len(list)
    listTem:=make([]string,length)
    for i,x:=range list{
        tem:=strings.Replace(x,".","e",-1)+"d"
        tem=strings.Replace(tem,"alpha","a",1)
        tem=strings.Replace(tem,"beta","b",1)
        tem=strings.Replace(tem,"rc","c",1)
        listTem[i]=tem+"@"+x
    }
    sort.Sort(VersionListSort(listTem))
    for i,x:=range listTem{
        index:=strings.Index(x,"@")
        list[i]=x[index+1:]
    }
    return list
}

type VersionListSort []string
func (p VersionListSort) Len() int           { return len(p) }
func (p VersionListSort) Less(i, j int) bool { return p[i] < p[j] }
func (p VersionListSort) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
wx163
#28 wx163 • 2016-01-21 10:42

没怎么写过golang, 不对的地方, 请指正

package main

import (
    . "fmt"
    "sort"
    "strings"
)

var verMapping = map[string]string{"alpha": "1", "beta": "2", "rc": "3", "release": "4"}

type Version struct {
    ser  string // 版本序列号
    ver  string // 版本号rc, alpha, beta, release(发布版本)
    name string
}

func (v Version) String() string { return v.name }

func NewVersion(s string) Version {
    i := 0
    for ; i < len(s); i++ {
        if (s[i] >= '0' && s[i] <= '9') || s[i] == '.' {
            continue
        }
        break
    }
    j := i
    for ; j < len(s); j++ {
        if (s[j] <= 'Z' && s[j] >= 'A') || (s[j] <= 'z' && s[j] >= 'a') {
            continue
        }
        break
    }
    ver := verMapping["release"]
    ser := s
    if j > i {
        arr := s[i:j]
        v, ok := verMapping[string(arr)]
        if !ok {
            panic(Sprintf("not valid Version string, %s", arr))
        }
        ver = v[:] + s[j:]
        ser = s[:i]
    }
    return Version{ser: ser, ver: ver, name: s}
}

func sameBigVersion(a, b string) bool {
    if len(a) > len(b) {
        return strings.HasPrefix(a, b)
    }
    return strings.HasPrefix(b, a)

}

type Versions []Version

func (v Versions) Len() int { return len(v) }

func (v Versions) Swap(i, j int) { v[i], v[j] = v[j], v[i] }

func (v Versions) Less(i, j int) bool {
    if sameBigVersion(v[i].ser, v[j].ser) {
        return v[i].ver <= v[j].ver
    }
    return v[i].ser <= v[j].ser
}

func main() {
    arr := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1", "10.0.1"}
    v := make(Versions, 0)
    for i := 0; i < len(arr); i++ {
        v = append(v, NewVersion(arr[i]))
    }
    Println(v)
    sort.Sort(v)
    Println(v)
}
David
#29 David • 2016-01-22 15:43

Just for fun:

import (
  "sort"
  "strings"
)
func split(v string) []string {
  for _, w := range []string{"rc", "alpha", "beta"} {
    v = strings.Replace(v, w, "."+w+".", 1)
  }
  return strings.Split(v, ".")
}
type Versions []string
func (vs Versions) Len() int      { return len(vs) }
func (vs Versions) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }

func (vs Versions) Less(i, j int) bool {
  ps1, ps2 := split(vs[i]), split(vs[j])
  if len(ps1) > len(ps2) {
    return !vs.Less(j, i)
  }
  for k := range ps1 {
    if ps1[k] == ps2[k] {
      continue
    }
    if ps1[k] > "a" && ps2[k] > "a" {
      return ps1[k] < ps2[k]
    }
    if ps1[k] < "a" && ps2[k] < "a" {
      return len(ps1[k]) < len(ps2[k]) || len(ps1[k]) == len(ps2[k]) && ps1[k] < ps2[k]
    }
    return ps1[k] > "a"
  }
  return ps2[len(ps1)] < "a"
}
func SortVersions(vs []string) {
  sort.Sort(Versions(vs))
}
guange
#30 guange • 2016-01-23 16:32
import (
    "testing"
)

func isLetter(a byte) int {
    if (a =='a' || a=='b' || a=='r') {
        return 1
    }
    return 0
}

func compare(a string, b string)(x int){
    as := []byte(a)
    bs := []byte(b)
    minLen := len(as)
    if len(bs)<minLen{
        minLen = len(bs)
    }

    x = 0 //默认相等
    for i :=0; i <minLen ; i++ {
        if(as[i]==bs[i]){
            continue
        }
        x = isLetter(bs[i]) - isLetter(as[i])
        if x != 0{
            return
        }

        if(as[i]>bs[i]){
            return 1
        } else if(as[i]<bs[i]) {
            return -1
        }

    }
    if x == 0{
        if(len(as) >minLen && isLetter(as[minLen])==1){
            x = -1
        } else if(len(bs) >minLen && isLetter(bs[minLen])==1){
            x = 1
        } else {
            x = len(as)-len(bs)
        }
    }
    return x
}

func VersionSort(versions []string) []string {
    //普通的冒泡 
    for i := 0; i<len(versions);i++  {
        for j := i+1; j<len(versions);j++  {
            if(compare(versions[i], versions[j])>0){
                versions[i], versions[j] = versions[j], versions[i]
            }
        }
    }
    return versions
}

func Test_VersionSort(t *testing.T)  {
    inputs := []string {"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    outputs := []string {"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}

    a := VersionSort(inputs)
    for i, num := range a {
        if num != outputs[i]{
            t.Error( "sort different")
        }
    }
}
hongkangzy
#31 hongkangzy • 2016-02-03 07:59

这么火啊

wangkechun
#32 wangkechun • 2016-02-05 11:44

来个python版本的

s=["1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1",
   "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", 
   "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"]

from functools import *
def deal(s):
    def calc(s):
        s = [int(i) for i in s.replace('alpha','.-3.').replace('beta','.-2.').replace('rc','.-1.').split('.')]
        return reduce(lambda a,b:a*100+b, s)*100**(5-len(s))
    return [i[1] for i in sorted(list(zip([calc(i) for i in s], s)))]

print(deal(s))
jimmykuu
#33 jimmykuu • 2016-02-05 15:46

春节后公布结果哈。

hil2000
#34 hil2000 • 2016-02-08 18:42

学习了!

qgymje
#35 qgymje • 2016-02-08 20:22
package main

import (
    "fmt"
    "sort"
    "strconv"
    "strings"
)

func main() {
    versions := []string{"10.0.1.1", "10.0.1", "1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    results := []string{"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1", "10.0.1", "10.0.1.1"}
    r := VersionSort(versions)
    for i, v := range results {
        if v != r[i] {
            panic("error")
        }
    }

    fmt.Println("right")
}

var testVersionMap = map[string]int{
    "alpha": -3,
    "beta":  -2,
    "rc":    -1,
}

type MinorVersion struct {
    Minor int
    Extra int
    Build int
}

func NewMinorVersion(input string) *MinorVersion {
    mv := new(MinorVersion)
    return mv.Parse(input)
}

func (mv *MinorVersion) Parse(input string) *MinorVersion {
    hasTestVersion := false
    for value, key := range testVersionMap {
        if strings.Contains(input, value) {
            index := strings.Index(input, value)
            mv.Minor, _ = strconv.Atoi(input[0:index])
            mv.Extra = key
            buildIndex := index + len(value)
            mv.Build, _ = strconv.Atoi(input[buildIndex:])
            hasTestVersion = true
            break
        }
    }
    if !hasTestVersion {
        mv.Minor, _ = strconv.Atoi(input)
    }
    return mv
}

func (mv MinorVersion) Parsed() []int {
    return []int{mv.Minor, mv.Extra, mv.Build}
}

type Version struct {
    Major  int
    Minor  *MinorVersion
    Build  int
    Origin string
}

func NewVersion(input string) *Version {
    version := new(Version)
    version.Origin = input
    return version.Parse(input)
}

func (v *Version) Parse(input string) *Version {
    if strings.Contains(input, ".") {
        versionParts := strings.Split(input, ".")
        v.Major, _ = strconv.Atoi(versionParts[0])
        v.Minor = NewMinorVersion(versionParts[1])
        if len(versionParts) > 2 {
            v.Build, _ = strconv.Atoi(versionParts[2])
        }
    } else {
        v.Major, _ = strconv.Atoi(input)
    }
    return v
}

func (v *Version) Parsed() (r []int) {
    r = append(r, v.Major)
    r = append(r, v.Minor.Parsed()...)
    r = append(r, v.Build)
    return
}

func (v *Version) String() string {
    return v.Origin
}

type Versions []*Version

func (vs Versions) Len() int {
    return len(vs)
}
func (vs Versions) Swap(i, j int) {
    vs[i], vs[j] = vs[j], vs[i]
}

func less(v1, v2 []int) bool {
    if len(v1) == 0 {
        return false
    }

    i1, i2 := v1[0], v2[0]

    if i1 < i2 {
        return true
    } else if i1 > i2 {
        return false
    } else {
        vv1 := v1[1:]
        vv2 := v2[1:]
        return less(vv1, vv2)
    }
    return false
}

func (vs Versions) Less(i, j int) bool {
    v1 := vs[i].Parsed()
    v2 := vs[j].Parsed()
    r := less(v1, v2)
    return r
}

func (vs *Versions) StringSlice() (r []string) {
    for _, v := range *vs {
        r = append(r, v.Origin)
    }
    return
}

func VersionSort(inputs []string) []string {
    versions := make([]*Version, 0, len(inputs))
    for _, input := range inputs {
        version := NewVersion(input)
        versions = append(versions, version)
    }

    vss := Versions(versions)
    sort.Sort(vss)
    return vss.StringSlice()
}
Unknown
#36 Unknown • 2016-02-09 05:45
guantou
#37 guantou • 2016-02-13 22:24

有点看不下去了,php一个 asort 搞定, golang却要一大堆代码。不是挑事,但php做得确实太贴心。

$versions = array("1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1");
asort($versions);
print_r($versions);
exit;
damao
#38 damao • 2016-02-15 19:32

@guantou 我也有点看不下去了,你仔细看看你的输出是不是符合要求先

wuqingtao
#39 wuqingtao • 2016-02-18 23:37

我也来一个

package main

import (
    "sort"
    "fmt"
    "strings"
)

var version = []string{
    "1.6beta1",
    "1.5rc1", 
    "1.5beta2",
    "1.5beta1", 
    "1.5.1", 
    "1.5", 
    "1.4rc2", 
    "1.4rc1", 
    "1.4beta1",
    "1.4.2", 
    "1.4.1",
    "1.4", 
    "1.3rc2",
    "1.3rc1", 
    "1.3beta2", 
    "1.3beta1", 
    "1.3.3", 
    "1.3.2", 
    "1.3.1", 
    "1.3", 
    "1.2rc5", 
    "1.2rc4", 
    "1.2rc3", 
    "1.2rc2", 
    "1.2rc1", 
    "1.2.2", 
    "1.2.1", 
    "1.2", 
    "1.1.2", 
    "1.1.1",
    "1.1", 
    "1.0.3",
    "1.0.2", 
    "1.5.2", 
    "1.5alpha1",
}

type Version []string

func (v Version) Len() int {
    return len(v)
}

func (v Version) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}

func (v Version) Less(i, j int) bool {
    r := strings.NewReplacer("alpha","a","beta","b","rc","r","","z")
    return r.Replace(v[i]) < r.Replace(v[j])
}

func VersionSort(versions []string) []string {
    sort.Sort(Version(versions))
    return versions
}

func main() {
    fmt.Println(VersionSort(version))
}
D:\go\src\sortversion>sortversion.exe
[1.0.2 1.0.3 1.1 1.1.1 1.1.2 1.2rc1 1.2rc2 1.2rc3 1.2rc4 1.2rc5 1.2 1.2.1 1.2.2
1.3beta1 1.3beta2 1.3rc1 1.3rc2 1.3 1.3.1 1.3.2 1.3.3 1.4beta1 1.4rc1 1.4rc2 1.4
 1.4.1 1.4.2 1.5alpha1 1.5beta1 1.5beta2 1.5rc1 1.5 1.5.1 1.5.2 1.6beta1]
liuferi
#40 liuferi • 2016-02-19 20:21

可能会出现1.10 么?

lvhao0419
#41 lvhao0419 • 2016-03-10 17:58
// VersionSort
package main

import (
    "fmt"
    "reflect"
    "sort"
    "strconv"
    "strings"
)

const (
    MAX_NUM     = 1000
    LEVEL1      = 1
    LEVEL2      = 1 * MAX_NUM
    LEVEL3      = 1 * MAX_NUM * MAX_NUM
    LEVEL_ALPHA = 0.97
    LEVEL_BETA  = 0.98
    LEVEL_RC    = 0.99
)

func GetNum(str string) int {
    i, _ := strconv.Atoi(str)
    return i
}

func VersionSort(versions []string) []string {
    m := make(map[int]string, len(versions))
    s := make([]int, len(versions))
    r := make([]string, len(versions))
    var tmp []string
    var num int
    for i, str := range versions {
        old := str
        switch {
        case strings.Index(str, "alpha") != -1:
            str = strings.Replace(str, "alpha", ".", -1)
            tmp = strings.Split(str, ".")

            num = LEVEL3*GetNum(tmp[0]) + LEVEL2*LEVEL_ALPHA*GetNum(tmp[1]) + LEVEL1*GetNum(tmp[2])
        case strings.Index(str, "beta") != -1:
            str = strings.Replace(str, "beta", ".", -1)
            tmp = strings.Split(str, ".")

            num = LEVEL3*GetNum(tmp[0]) + LEVEL2*LEVEL_BETA*GetNum(tmp[1]) + LEVEL1*GetNum(tmp[2])
        case strings.Index(str, "rc") != -1:
            str = strings.Replace(str, "rc", ".", -1)
            tmp = strings.Split(str, ".")

            num = LEVEL3*GetNum(tmp[0]) + LEVEL2*LEVEL_RC*GetNum(tmp[1]) + LEVEL1*GetNum(tmp[2])
        case strings.Index(str, ".") == strings.LastIndex(str, "."):
            str += ".0"
            tmp = strings.Split(str, ".")

            num = LEVEL3*GetNum(tmp[0]) + LEVEL2*GetNum(tmp[1]) + LEVEL1*GetNum(tmp[2])
        default:
            tmp = strings.Split(str, ".")

            num = LEVEL3*GetNum(tmp[0]) + LEVEL2*GetNum(tmp[1]) + LEVEL1*GetNum(tmp[2])
        }

        fmt.Println(old, "->", str, "->", num)
        m[num] = old
        s[i] = num
    }

    sort.Ints(s)

    fmt.Println("=================")
    for i, num := range s {
        r[i] = m[num]
        fmt.Println(i, "->", num, "->", m[num])
    }
    return r
}

func main() {
    before := []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    after := []string{"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}
    ret := VersionSort(before)
    fmt.Println(reflect.DeepEqual(after, ret))
}

提供一种思路吧..

ThoseFlowers
#42 ThoseFlowers • 2016-03-10 21:04

@liuferi 可能。这里的点(“.”)不是小数点,而是分隔符。第一个后面的数表示副版本号,它有可能是两位数。

xingfe123
#43 xingfe123 • 2016-05-20 15:37

为什么大家这么喜欢替换呢,直接比较不好吗?@lvhao0419@wuqingtao


type versionSort []string

func (v versionSort) Len() int {
    return len(versionSort)
}
func (v versionSort) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}
func (v versionSort) Less(i, j int) bool {
    return versionLess(v[i], v[j])
}

func VersionSort(versions []string) []string {
    sort.Sort(versionSort(versions))
    return versions
}


func vesionLess(left, right string) bool {
    i := 0
    for i < len(left) && i < len(right) {
        if left[i] == right[i] {
            i++
            if left[i] == 'a' { //alpha
                i += 4
            } else if left[i] == 'b' { //beta
                i += 3
            } else if left[i] == 'r' { //rc
                i++
            }
        }else if left[i] < right[i] {
            return true
        } else {
            return false
        }
    }
    return false
}


func vesionLess10(left, right string) bool {
    findnext := func(str string, start int) int {
        i := start
        for ; i < len(str); i++ {
            if str[i] == '.' {
                return i
            }
        }
        return i
    }
    i := 0
    for i < len(left) && i < len(right) {
        l := findnext(left)
        r := findnext(right)
        if l < r {
            return true
        } else if r < l {
            return false
        } else if left[i] == right[i] {
            i++
            if left[i] == 'a' { //alpha
                i += 4
            } else if left[i] == 'b' { //beta
                i += 3
            } else if left[i] == 'r' { //rc
                i++
            }
        } else if left[i] < right[i] {
            return true
        } else {
            return false
        }
    }
    return false
}
reganee
#44 reganee • 2016-05-22 23:21

感觉使用递归更好解决
每次以.为分隔符

package main

import (
    "fmt"
    "sort"
    "strings"
)

var (
    TestData   = []string{"1.6beta1", "1.5rc1", "1.5beta2", "1.5beta1", "1.5.1", "1.5", "1.4rc2", "1.4rc1", "1.4beta1", "1.4.2", "1.4.1", "1.4", "1.3rc2", "1.3rc1", "1.3beta2", "1.3beta1", "1.3.3", "1.3.2", "1.3.1", "1.3", "1.2rc5", "1.2rc4", "1.2rc3", "1.2rc2", "1.2rc1", "1.2.2", "1.2.1", "1.2", "1.1.2", "1.1.1", "1.1", "1.0.3", "1.0.2", "1.5.2", "1.5alpha1"}
    TestResult = []string{"1.0.2", "1.0.3", "1.1", "1.1.1", "1.1.2", "1.2rc1", "1.2rc2", "1.2rc3", "1.2rc4", "1.2rc5", "1.2", "1.2.1", "1.2.2", "1.3beta1", "1.3beta2", "1.3rc1", "1.3rc2", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4beta1", "1.4rc1", "1.4rc2", "1.4", "1.4.1", "1.4.2", "1.5alpha1", "1.5beta1", "1.5beta2", "1.5rc1", "1.5", "1.5.1", "1.5.2", "1.6beta1"}
)

func main() {
    sort.Sort(VersionSort(TestData))
    for i := 0; i < len(TestData); i++ {
        if TestData[i] != TestResult[i] {
            fmt.Println(false)
            return
        }
    }
    fmt.Println("ok")
}

type VersionSort []string

func (v VersionSort) Len() int {
    return len(v)
}
func (v VersionSort) Swap(i, j int) {
    v[i], v[j] = v[j], v[i]
}
func (v VersionSort) Less(i, j int) bool {
    return less(v[i], v[j])
}

func less(a, b string) bool {
    aSep, anext := Sep(a)
    bSep, bnext := Sep(b)
    if aSep == bSep {
        for i := 0; i < aSep; i++ {
            if a[i] != b[i] {
                return a[i] < b[i]
            }
        }
        if anext != bnext && anext {
            return false
        }
        if anext == bnext {
            return less(a[aSep+1:], b[bSep+1:])
        }
    }
    for i := 0; i < max(aSep, bSep); i++ {
        if i >= aSep && i < bSep && b[i] > 57 {
            return false
        }
        if i < aSep && i < bSep {
            if a[i] != b[i] {
                return a[i] < b[i]
            }
        }
    }
    return true
}

func max(a, b int) int {
    if a >= b {
        return a
    }
    return b
}

func Sep(str string) (int, bool) {
    sep := strings.Index(str, ".")
    if sep == -1 {
        return len(str), false
    }
    return sep, true
}
lmw
#45 lmw • 2016-05-23 22:00
func VersionSort(versions []string) []string {

    start := time.Now().UnixNano()

    varsionCode := [][]int{}
    for i := 0; i < len(versionArr); i++ {
        str := strings.Replace(versionArr[i], ".", "", -1)
        a := "0"
        arr := []string{}
        if strings.Contains(str, "alpha") {
            arr = strings.Split(str, "alpha")
            a = "1"
            str = arr[0]

        } else if strings.Contains(str, "beta") {
            a = "2"
            arr = strings.Split(str, "beta")
            str = arr[0]

        } else if strings.Contains(str, "rc") {
            a = "3"
            arr = strings.Split(str, "rc")
            str = arr[0]
        }

        if len(str) == 2 {
            str += "00"
        }
        if len(str) == 3 {
            str += "0"
        }

        if len(arr) == 2 {
            str = str + a + arr[1]
        } else {
            str += "99"
        }

        code, _ := strconv.Atoi(str)

        varsionCode = append(varsionCode, []int{code, i})
    }

    var temp []int
    for j := 0; j < len(varsionCode); j++ {
        for k := 0; k < len(varsionCode)-1; k++ {

            if varsionCode[k+1][0] < varsionCode[k][0] {

                temp = varsionCode[k+1]

                varsionCode[k+1] = varsionCode[k]

                varsionCode[k] = temp

            }
        }
    }

    var newVersions = []string{}

    for n := 0; n < len(varsionCode); n++ {

        newVersions = append(newVersions, versionArr[varsionCode[n][1]])

    }

    fmt.Printf("用时:%d\n", time.Now().UnixNano()-start)
    fmt.Println(newVersions)

    return newVersions
}
xd42
#46 xd42 • 2016-05-24 09:14

package main

import (
“fmt”
“sort”
“strings”
)

type MySort struct {
Value string
}

type MySlice [] MySort

func (t MySlice) Len() int {
return len(t)
}

func (t MySlice) Swap(i int,j int) {
t[i],t[j] = t[j],t[i]
}

func (t MySlice) Less(i int,j int) bool {
result := strings.Compare(t[j].Value,t[i].Value )
if result < 0 {
return true
} else {
return false
}
}

func main() {
tt := [] MySort{
{“1.6beta1”}, {“1.5rc1”}, {“1.5beta2”}, {“1.5beta1”}, {“1.5.1”}, {“1.5”},
{“1.4rc2”}, {“1.4rc1”}, {“1.4beta1”}, {“1.4.2”}, {“1.4.1”}, {“1.4”}, {“1.3rc2”},
{“1.3rc1”}, {“1.3beta2”}, {“1.3beta1”}, {“1.3.3”}, {“1.3.2”}, {“1.3.1”}, {“1.3”},
{ “1.2rc5”}, {“1.2rc4”}, {“1.2rc3”}, {“1.2rc2”}, {“1.2rc1”}, {“1.2.2”}, {“1.2.1”},
{ “1.2”}, {“1.2.0”},{“1.1.2”}, {“1.1.1”}, {“1.1”}, {“1.0.3”}, {“1.0.2”}, {“1.5.2”}, {“1.5alpha1”},
}

fmt.Println(tt)
fmt.Println(“the Compare result : ——————————“)
sort.Sort(sort.Reverse(MySlice(tt)))
fmt.Println(tt)
}

需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。