Sort an array with strings and numbers - go

I passed few hours searching about a lib or a way to do this:
I have an array []string{"ex10", "ex2", "ex1"} and after call the sort.Strings or using sort.Slice to compare with an array of bytes I get the result []string{"ex1", "ex10", "ex2"}
anyone has an idea how to implement this?
if anyone knows swift I've found the solution here

If you don't want the numbers sorted lexicographically, you need to separate them from the strings and parse them as integers.
This example assumes there is always a 2 letter prefix with a valid number, and slices the string accordingly: https://play.golang.org/p/LaX-pTr6etc
s := []string{"ex10", "ex2", "ex1"}
sort.Slice(s, func(i, j int) bool {
if s[i][:2] != s[j][:2] {
return s[i] < s[j]
}
ii, _ := strconv.Atoi(s[i][2:])
jj, _ := strconv.Atoi(s[j][2:])
return ii < jj
})

Related

How can I sort string array by character a in golang

I am working on an algorithm question and I need to encode it with golang. In this question I need to sort a given string array by character 'a'. If I need to talk about the details of the question.
Question:
Write a function that sorts a bunch of words by the number of character “a”s within the
word (decreasing order). If some words contain the same amount of character “a”s then you
need to sort those words by their lengths
Input
["aaaasd", "a", "aab", "aaabcd", "ef", "cssssssd", "fdz", "kf", "zc", "lklklklklklklklkl", "l"]
Output:
["aaaasd", "aaabcd", "aab", "a", "lklklklklklklklkl", "cssssssd", "fdz", "ef", "kf", "zc", "l"]
My Solution:
func main() {
arr := []string{"aaaasd", "a", "aab", "aaabcd", "ef", "cssssssd", "fdz", "kf", "zc", "lklklklklklklklkl", "l"}
fmt.Println(mostFrequent(arr))
}
type FrequencyAndLength struct {
slice string
mostFrequent int
len int
}
func mostFrequent(arr []string) []FrequencyAndLength { // assuming no
testArray := []FrequencyAndLength{}
for _, a := range arr {
testArray = append(testArray, FrequencyAndLength{
slice: a,
mostFrequent: strings.Count(a, "a"),
len: len(a),
})
}
fmt.Println(testArray)
return testArray
}
I'm currently getting the number of a and the length of each element in it. I need to sort first by the number of a, then by length if there are even numbers of a, in descending order, but logically I'm stuck here.
Use sort.Slice() to sort any slice by a custom logic. This function expects a function that defines the "less" relation between 2 elements.
In your case a value is less than another if it contains more a characters, or if the count is equal, then resort to comparing their lengths. To count substrings, use strings.Count(). To get the length of a string, use the builtin len() function, but note that len() returns the UTF-8 encoded byte length, not the number of runes. For the letter, use utf8.RuneCountInString().
For example:
in := []string{"aaaasd", "a", "aab", "aaabcd", "ef", "cssssssd", "fdz", "kf", "zc", "lklklklklklklklkl", "l"}
sort.Slice(in, func(i, j int) bool {
s1, s2 := in[i], in[j]
count1, count2 := strings.Count(s1, "a"), strings.Count(s2, "a")
if count1 != count2 {
return count1 > count2
}
return utf8.RuneCountInString(s1) > utf8.RuneCountInString(s2)
})
fmt.Println(in)
This will output (try it on the Go Playground):
[aaaasd aaabcd aab a lklklklklklklklkl cssssssd fdz ef kf zc l]
Note that the order between elements that contain equal number of a's and have equal length is unspecified. If you want them in the same order as in your input slice, use sort.SliceStable() instead of sort.Slice().
Also note that our custom logic is not complex but not trivial either. The function may be called many times to compare elements, and the same element may be passed (asked) multiple times. If the input slice is big, it may be profitable to calculate the numer of a's and the rune length once for each element, store them in a map for example, and just query this precalculated data in the less() function.
This is how it could look like:
// Pre-calculate
type info struct{ count, length int }
calculated := map[string]info{}
for _, s := range in {
calculated[s] = info{
count: strings.Count(s, "a"),
length: utf8.RuneCountInString(s),
}
}
sort.Slice(in, func(i, j int) bool {
inf1, inf2 := calculated[in[i]], calculated[in[j]]
if inf1.count != inf2.count {
return inf1.count > inf2.count
}
return inf1.length > inf2.length
})
This outputs the same. Try it on the Go Playground.

Sorting a slice based on the order of the elements in another slice

I am attempting to order a slice based on the order of the elements within another slice. My sort function works when I only have one of each type within my slice I want to order however when I start adding more elements the ordering breaks.
I have created an example within the Golang playground.
https://play.golang.org/p/e9sHIeV2qSf
I want to order my Variant slice by the Code field and have it the same as order as the codes appear in the Language struct.
Below is the sort function I am using:
sort.Slice(variants, func(i, j int) bool {
for k, language := range languages {
if language.Code == variants[i].Code {
return i >= k
}
}
return false
})
The current order it's returning is:
Sorted slice: [{Code:en-GB} {Code:en-US} {Code:en-GB} {Code:es-ES}
{Code:en-GB} {Code:en-GB} {Code:en-GB} {Code:en-GB} {Code:es-ES}]
When the order within my Language struct is:
"en-GB", "en-US", "fr-FR", "es-ES"
I think to do this, you need to build a ranking of your languages:
var langMap map[string]int
for i, lang := range languages {
langMap[lang.Code] = i
}
With this, it becomes trivial to just look up the ranking of each item in variants, and return the appropriate value:
sort.Slice(variants, func(i, j int) bool {
iRank, jRank := langMap[variants[i].Code], langMap[variants[j].Code]
return iRank < jRank
})
If there's a chance you may have inputs that are not in the pre-sorted list, you can sort them last:
sort.Slice(variants, func(i, j int) bool {
iRank, iExists := langMap[variants[i].Code]
jRank, jExists := langMap[variants[j].Code]
switch (
case iExists && jExists:
// Both exist in the pre-ordered list, so sort by rank
return iRank < jRank
case !iExists && !jExists:
// Neither exists in the pre-ordered list, sort alphabetically
return variants[i].Code < variants[j].Code
case iExists:
// Only i exists, so sort it before j
return true
default: // jExists
// Only j exists, so sort it after i
return false
)
})
It is logically possible to do the same by looping through your reference list each time, as you're attempting, but it's much harder to reason about, and far less efficient.

How to find the distance between two runes

I'm trying to solve a couple of example programming problems to familiarize myself with the language.
I am iterating over a string as follows:
func main() {
fullFile := "abcdDefF"
for i := 1; i < len(fullFile); i++ {
println(fullFile[i-1], fullFile[i], fullFile[i-1]-fullFile[i])
}
}
In the loop I want to get the difference between the current rune and the previous rune (trying to identify lower-case - upper-case pairs by finding any pairs where the difference is == 32.
Strangely, the subtraction doesn't work properly (in fact seems to yield addition in cases where I would expect a negative number) although I would expect it to since runes are represented by int32.
Figured it out: the data type returned was a byte.
Explicitly converted to int and everything works as expected.
func main() {
fullFile, _ := ioutil.ReadFile("input/input.txt")
previous := 0
current := 0
for i := 1; i < len(fullFile); i++ {
previous = int(fullFile[i-1])
current = int(fullFile[i])
println(current, previous, current-previous)
}
}

How can I identify a matching pattern for a given number using go?

I'm trying to identify pattern matches for a given telephone number range to use in a Cisco Communications Manager platform.
Essentially, an 'X' matches the numbers 0-9 in a telephone number, and you can specify a range of digits using the [x-y] notation.
Given a telephone number range of 02072221000-02072221149 consisting of 150 numbers, this would create and should output two patterns: 020722210XX and 020722211[0-4]X
Obviously I'd like it to work on any range provided. I just can't seem to get my head around how to generate those patterns given the number ranges.
Any thoughts would be greatly appreciated.
Many thanks.
I believe I found a decent algorithm which should handle this for you. I apologize ahead of time if any of the explanation isn't detailed enough, but a lot of this came to intuition which can be hard to explain.
I started with more simplified cases, figuring out a method for how to get the fewest number of patterns from a comparison. For my examples I'll be comparing 211234 to 245245.
After a bit of thinking I worked out that you need to take the range of numbers from the smaller number up to 9 and handle the special case for the lowest digit in the smaller number. To explain in a bit more detail, in the number 211234 the ideal is to represent the last digit as an X but we can only do that for cases where the digit may be [0-9] the only case in this example where we can't use [0-9] is when our tens digit is 3 because we have a lower limit of 4. This logic then propagates up the rest of the number as we head toward the most significant digit. So for the tens digit in the next case we have a lower bound based on the previous example of 4 because we're handling the case when we allow a 3 specially. So for our tens range we end up with a 4-9 because the next digit over does not restrict our range.
In fact we won't be restricted until the most significant digit which is bounded by the numbers in the range between the numbers we're comparing. After working a few problems out by hand I noticed a bit of a pattern of the pyramid of Xs in the cases where the numbers digits were significantly apart:
compare: 211234
to: 245245
21123[4-9]
2112[4-9]X
211[3-9]XX
21[2-9]XXX
2[2-3]XXXX
24[0-4]XXX
245[0-1]XX
2452[0-3]X
24514[0-5]
This was my first hint as to how to handle it. Starting from the least significant moving up, taking advantage of the symmetry, but handling the case where we hit the "top of the pyramid". This example is easy though, there are many corner cases which will cause issues. For the sake of brevity I'm not going to go into detail for each but I'll give a short explanation for each:
What do you do when the 2 compared digits has one number between them, such as between 4 and 6?
In this case simply use the single digit in place of a range.
What do you do when the 2 compared digits have no number between them, such as between 4 and 5?
In this case throw away the row in which you'd handle the numbers between the digits as all cases will be handled explicitly.
What do you do when the minimum number in the range is 8?
In this case when we add 1 to the number to get a lower bound for the range we get a 9, which means we can simply substitute in a 9 rather than a range of [9-9]
What do you do when the minimum number in the range is 9?
In this case we simply don't bother handling that number as when handling the next digit up it should be covered by its use of X
I'm sure I'm missing some corner cases which I handle in the code which I simply didn't think to put in this list. I'm willing to clarify any part of the code if you just leave a comment asking.
Below is my stab at it in Go. It could probably be a bit more DRY but this is what I came up with after fiddling for a bit. I'm also pretty new to Go so please notify me of any spirit fouls in the comments and I'll correct them.
I don't guarantee this will handle every case, but it handled every case I threw at it. It's up to you to turn it into a script which takes in 2 strings ;)
Edit: I just realized via the example in the question (which for some reason I never ran) that this doesn't always condense the provided range in to the smallest number of outputs, but it should always give patterns which cover every case. Despite this drawback I think it's a good step in the right direction for you to work on top of. I'll update the answer if I find the time to get it to condense cases where the previous range is 1-9 and the special case is 0. The best means for which might end up being after the initial generation condensing these cases "manually".
package main
import (
"strconv"
"fmt"
)
func getStringFromMinAndMax(min int, max int) (string, bool){
minstr := strconv.Itoa(min)
maxstr := strconv.Itoa(max)
if max == min {
return minstr, false
}
if max < min{
return minstr, false
}
return "["+minstr+"-"+maxstr+"]", true
}
func main(){
str1 := "211234"
str2 := "245245"
diffLength := 0
for i := 0; i < len(str1); i++{
diffLength = i+1
number1, _ := strconv.Atoi(str1[:len(str1)-i-1])
number2, _ := strconv.Atoi(str2[:len(str2)-i-1])
if number1 == number2 {
break
}
}
elems := (diffLength * 2)-1
output := make([]*[]string, elems+1)
for i := 0; i < elems; i++ {
newSlice := make([]string, diffLength)
output[i] = &newSlice
}
for digit := 0; digit < diffLength; digit++ {
for j := 0; j < diffLength; j++ {
if j == digit {
if output[j] != nil {
min, _ := strconv.Atoi(string(str1[len(str1)-(digit+1)]))
max := 9
if digit == diffLength-1 {
max, _ = strconv.Atoi(string(str2[len(str1)-(digit+1)]))
max = max - 1
}
if digit != 0{
min = min+1
}
if min < 10 {
maxchar := strconv.Itoa(max)[0]
minchar := strconv.Itoa(min)[0]
newVal, safe := getStringFromMinAndMax(min, max)
if digit == diffLength-1 && !safe && (str1[len(str1)-(digit+1)] == maxchar || str2[len(str2)-(digit+1)] == minchar) {
output[j] = nil
} else {
(*output[j])[diffLength-digit-1] = newVal
}
} else {
output[j] = nil
}
}
if j != diffLength-1 && output[elems-1-j] != nil {
min := 0
max, _ := strconv.Atoi(string(str2[len(str1)-(digit+1)]))
if digit != 0{
max = max-1
}
if max >= 0{
newVal, _ := getStringFromMinAndMax(min, max)
(*output[elems-1-j])[diffLength-digit-1] = newVal
} else {
output[elems-1-j] = nil
}
}
} else {
if j > digit {
if output[j] != nil {
(*output[j])[diffLength-digit-1] = "X"
}
if j != diffLength-1 && output[elems-1-j] != nil {
(*output[elems-1-j])[diffLength-digit-1] = "X"
}
} else {
if output[j] != nil {
(*output[j])[diffLength-digit-1] = string(str1[len(str1)-digit-1])
}
if j != diffLength-1 && output[elems-1-j] != nil {
(*output[elems-1-j])[diffLength-digit-1] = string(str2[len(str2)-digit-1])
}
}
}
}
}
for _, list := range output {
if list != nil{
if len(str1) != diffLength{
fmt.Printf(str1[:len(str1)-diffLength])
}
for _, element := range *list {
fmt.Printf(element)
}
fmt.Printf("\n")
}
}
}
Footnotes:
diffLength is the number of characters on the end of the strings which differ, I couldn't think of a better way to get this number than what's in the script...
Me setting an output to nil is me saying, "This one will be handled explicitly, so throw it away"
j is a variable for which output I'm setting... But this also gets mirrored to the bottom, so I couldn't think of a concise name to give it thus I left it j.
digit is tracking which digit from the right we are modifying

Initialize golang slice with int numbers from 0 to N

I am almost certain that I read about a simple "tricky" way to initialize slice of ints with the numbers from 0 to N, but I cannot find it anymore.
What is the simplest way to do this?
You just use make passing N for the length then use a simple for loop to set the values...
mySlice := make([]int, N)
for i := 0; i < N; i++ {
mySlice[i] = i
}
Here's a full example on play; https://play.golang.org/p/yvyzuWxN1M

Resources