# Semver sort

1 min read Tweet this post
Section titled Problem

## Problem

• given input array of string contains release version that must follow `x.y.z` form.
• `x.y.z` must be integer with positive values
• sort by latest release
Section titled Solution

## Solution

See in playground https://go.dev/play/p/_nFWNUOcDCu.

The process is straigtforward

• Parse string into semver struct
• Sort using custom slice and implement required sorting interface `Len`, `Swap` and `Less`, refers to https://pkg.go.dev/sort#example-package
• Revert back to array of string where version string follows `x.y.z` format
``````package main

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

// Semver represents a semantic version in the format x.y.z
type Semver struct {
Major int
Minor int
Patch int
}

func (s Semver) ToString() string {
return fmt.Sprintf("%d.%d.%d", s.Major, s.Minor, s.Patch)
}

// NewSemver returns a new Semver instance with the specified major, minor, and patch values.
func NewSemver(major int, minor int, patch int) Semver {
return Semver{
Major: major,
Minor: minor,
Patch: patch,
}
}

// ParseSemver takes a string representation of a semantic version in the format x.y.z and returns a new Semver instance.
func ParseSemver(version string) (Semver, error) {
parts := strings.Split(version, ".")
if len(parts) != 3 {
return Semver{}, fmt.Errorf("invalid version format: %s", version)
}

major, minor, patch := 0, 0, 0
var err error
if major, err = strconv.Atoi(parts[0]); err != nil || major < 0 {
return Semver{}, fmt.Errorf("invalid major version: %s", parts[0])
}
if minor, err = strconv.Atoi(parts[1]); err != nil || minor < 0 {
return Semver{}, fmt.Errorf("invalid minor version: %s", parts[1])
}
if patch, err = strconv.Atoi(parts[2]); err != nil || patch < 0 {
return Semver{}, fmt.Errorf("invalid patch version: %s", parts[2])
}

return NewSemver(major, minor, patch), nil
}

// Versions is a slice of Semver instances
type Versions []Semver

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

func (v Versions) Less(i, j int) bool {
if v[i].Major != v[j].Major {
return v[i].Major < v[j].Major
}
if v[i].Minor != v[j].Minor {
return v[i].Minor < v[j].Minor
}
return v[i].Patch < v[j].Patch
}

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

func solution(input []string) []string {
var versions Versions
for _, v := range input {
semver, err := ParseSemver(v)
if err == nil {
versions = append(versions, semver)
}
}

sort.Sort(sort.Reverse(versions))
result := []string{}
for _, v := range versions {
result = append(result, v.ToString())
}
return result
}

func main() {
input := []string{"1.0.0", "2.0.0", "0.1.0", "3.0.0", "invalid", "1.2.3", "1.2.3-rc", "1.-1.2"}

result := solution(input)
fmt.Println(result)
}
``````
go puzzle snippets