Home > Software engineering >  Golang - Sort string array by line length AND alphabetically at once
Golang - Sort string array by line length AND alphabetically at once

Time:11-05

I have seen this question asked in a few other languages, each with great answers. i.e.

How to sort a file by line length and then alphabetically for the second key?

how to sort by length of string followed by alphabetical order?

Sort strings alphabetically AND by length?

I can't figure it out in Golang :/ Say I have this list:

2 22 2H 2J 2J2 2J3 2J322422 2J322423 2J33 2M 2P 2W 2X

I want the sorted output to be:

2 22 2H 2J 2M 2P 2W 2X 2J2 2J3 2J33 2J322422 2J322423

I have tried a few things in Golang, but just can't get it working.

// Log
t.Log.Println(values)

// Sort values alphabetically
sort.Strings(values)

// Sort values by length
sort.Slice(values, func(i, j int) bool {
    return len(values[i]) < len(values[j])
})

// Log
t.Log.Println(values)

Thank you.

CodePudding user response:

If you sort twice, the second sorting will not take the rules of the first sorting into account.

You must sort once, and the sorting rule(s) must include all properties you want to sort by.

So primarily you want to sort by length. And if 2 elements have the same length, then by natural order. You can achieve this by first checking the lengths in the less() function. If they are not equal, then the order by length is what decides the result. If they are equal, you resort to natural order.

list := strings.Split("2 22 2H 2J 2J2 2J3 2J322422 2J322423 2J33 2M 2P 2W 2X", " ")
fmt.Println(list)

sort.Slice(list, func(i, j int) bool {
    l1, l2 := len(list[i]), len(list[j])
    if l1 != l2 {
        return l1 < l2
    }
    return list[i] < list[j]
})
fmt.Println(list)

This will output (try it on the Go Playground):

[2 22 2H 2J 2J2 2J3 2J322422 2J322423 2J33 2M 2P 2W 2X]
[2 22 2H 2J 2M 2P 2W 2X 2J2 2J3 2J33 2J322422 2J322423]

You can extend this logic to sort by any number of properties (or rules). You check the higher priority rules first, and if they define difference in order, you return the order designated by them (the information whether the ith element is less than the jth). If they don't differentiate the positions of the elements in question, you proceed with lower priority rules.

CodePudding user response:

You need a comparator function that compares properly according to the collation rules that you have defined.

To sort a slice of strings, first by length, and then alphabetically, something like this should do you:

strs := []string{
  "Zulu"    , "Yankee" , "X-Ray"  , "Whiskey" , "Victor" ,
  "Ulysses" , "Tango"  , "Sierra" , "Romeo"   , "Quebec" ,
  "Poppa"   ,"Oscar"   , "Nancy"  , "Mike"    , "Lima"   ,
  "Kilo"    , "Juliet" , "India"  , "Hotel"   , "Golf"   ,
  "Foxtrot" , "Echo"   , "Delta"  , "Charlie" , "Bravo"  ,
  "Alpha"   ,
  }

  byLengthThenAlphabetically := func(i int, j int) bool {
    x := strs[i]
    y := strs[j]
    deltaLength := len(x) - len(y)

    return deltaLength < 0 || (deltaLength == 0 && x < y)
  }
  
  sort.Slice(strs, byLengthThenAlphabetically )

Try it in the Go Playground at https://play.golang.org/p/nL2bTDlWM49

  • Related