I want to check if a string is numeric.
For example:
"abcd123" should return false.
"1.4" or "240" should return true.
I thought about using ParseInt and ParseFloat (from the strconv package), but am not sure if that is the right way.
I was thinking of using strconv ParseInt and ParseFloat but not sure
if that is the right way.
Well, it's certainly a right way.
You don't need to use ParseInt, though. ParseFloat will do the job.
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
See an example here: https://play.golang.org/p/D53HRS-KIL
If you need to convert the string to a floating-point number strconv.ParseFloat is the first choice.
Here you just need to know that there is only "0123456789" and maximum one '.' in your string, here for me isNumDot is 12x faster than isNumeric, see:
Consider this (1.7 seconds) - optimized for performance:
func isNumDot(s string) bool {
dotFound := false
for _, v := range s {
if v == '.' {
if dotFound {
return false
}
dotFound = true
} else if v < '0' || v > '9' {
return false
}
}
return true
}
and this (21.7 seconds - doing more extra works "converts the string to a floating-point number"):
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
Try it:
package main
import (
"fmt"
"strconv"
"time"
)
func isNumDot(s string) bool {
dotFound := false
for _, v := range s {
if v == '.' {
if dotFound {
return false
}
dotFound = true
} else if v < '0' || v > '9' {
return false
}
}
return true
}
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func main() {
fmt.Println(isNumDot("240")) //true
fmt.Println(isNumDot("abcd123")) //false
fmt.Println(isNumDot("0.4.")) //false
fmt.Println(isNumDot("240 ")) //false
benchmark(isNumDot)
benchmark(isNumeric)
}
func benchmark(f func(string) bool) {
var res bool
t := time.Now()
for i := 0; i < 100000000; i++ {
res = f("a 240") || f("abcd123") || f("0.4.") || f("240 ")
}
fmt.Println(time.Since(t))
fmt.Println(res)
}
output:
true
false
false
false
1.7822s
false
21.723s
false
Using the benchmark (isNumDot is faster than isNumeric):
BenchmarkIsNumDot-8 34117197 31.2 ns/op 0 B/op 0 allocs/op
BenchmarkIsNumeric-8 1931089 630 ns/op 192 B/op 4 allocs/op
// r = isNumDot("2.22")
BenchmarkIsNumDot-8 102849996 11.4 ns/op 0 B/op 0 allocs/op
BenchmarkIsNumeric-8 21994874 48.5 ns/op 0 B/op 0 allocs/op
// r = isNumDot("a 240")
BenchmarkIsNumDot-8 256610877 4.58 ns/op 0 B/op 0 allocs/op
BenchmarkIsNumeric-8 8962381 140 ns/op 48 B/op 1 allocs/op
The benchmark:
package main
import (
"testing"
)
var r bool
func BenchmarkIsNumDot(b *testing.B) {
for i := 0; i < b.N; i++ {
r = isNumDot("a 240") || isNumDot("abcd123") || isNumDot("0.4.") || isNumDot("240 ")
}
}
func BenchmarkIsNumeric(b *testing.B) {
for i := 0; i < b.N; i++ {
r = isNumeric("a 240") || isNumeric("abcd123") || isNumeric("0.4.") || isNumeric("240 ")
}
}
I tried to comment on Adrian's answer but I guess I don't have enough reputation points. Building on his excellent response, here is a variation using PCRE. Some brief explanation on the symbols if you are unfamiliar with regular expressions:
"^" matches the start of input (i.e. beginning of your string)
"$" matches the end of input (i.e. the end of your string)
"()" are grouping operators
"*" matches 0 or more
"+" matches 1 or more
"?" matches exactly 0 or 1
"\d" is a character class which represents the character values 0 through 9
So, the following would require at least a leading 0, permit "0.", and everything else that is normally identified as a floating point value. You can experiment with this a bit.
func isFloat(s string) bool {
return regexp.MatchString(`^\d+(\.\d*)?$`, s)
}
Naturally, if you are calling this function to validate data, it should be cleaned:
str := strings.TrimSpace(someString)
if isFloat(str) {
...
}
That only works on ASCII characters. If you are dealing with UTF8 or another multi-byte character set (MBCS), it can be done with regexp but more work would be required and perhaps another approach altogether.
You can use the strconv.Atoi function for check integer values, and the strconv.ParseFloat for float values. Below is an example:
package main
import (
"fmt"
"strconv"
)
func main() {
v1 := "14"
if _, err := strconv.Atoi(v1); err == nil {
fmt.Printf("%q looks like a number.\n", v1)
} else {
fmt.Printf("%q is not a number.\n", v1)
}
v2 := "1.4"
if _, err := strconv.ParseFloat(v2, 64); err == nil {
fmt.Printf("%q looks like a float.\n", v2)
} else {
fmt.Printf("%q is not a float.\n", v2)
}
}
/* Output:
"14" looks like a number.
"1.4" looks like a float.
*/
You can check it on the Go Playground.
All the answers are valid, but there's another option not yet suggested:
re := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`)
isNum := re.Match([]byte("ab123"))
Playground demo
I hit this in a high-throughput system today and did a benchmark of the three suggestions. Results:
BenchmarkNumDot-4: 657966132: 18.2 ns/op
BenchmarkNumeric-4: 49575919: 226 ns/op
BenchmarkRegexp-4: 18817201: 628 ns/op
Code follows since the playground does not support benchmarking.
package main
import (
"regexp"
"strconv"
"testing"
)
func BenchmarkNumDot(b *testing.B) {
for i := 0; i < b.N; i++ {
isNumDot("abc")
isNumDot("123")
isNumDot("12.34")
isNumDot("1.2.3.4")
}
}
func BenchmarkNumeric(b *testing.B) {
for i := 0; i < b.N; i++ {
isNumeric("abc")
isNumeric("123")
isNumeric("12.34")
isNumeric("1.2.3.4")
}
}
func BenchmarkRegexp(b *testing.B) {
re := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`)
for i := 0; i < b.N; i++ {
isNumReg("abc", re)
isNumReg("123", re)
isNumReg("12.34", re)
isNumReg("1.2.3.4", re)
}
}
func isNumDot(s string) bool {
dotFound := false
for _, v := range s {
if v == '.' {
if dotFound {
return false
}
dotFound = true
} else if v < '0' || v > '9' {
return false
}
}
return true
}
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func isNumReg(s string, re *regexp.Regexp) bool {
return re.Match([]byte(s))
}
Related
I'm benchmarking unmarshaling from string to int and uint with this code:
package main
import (
"strconv"
"testing"
)
func BenchmarkUnmarshalInt(b *testing.B) {
for i := 0; i < b.N; i++ {
UnmarshalInt("123456")
}
}
func BenchmarkUnmarshalUint(b *testing.B) {
for i := 0; i < b.N; i++ {
UnmarshalUint("123456")
}
}
func UnmarshalInt(v string) int {
i, _ := strconv.Atoi(v)
return i
}
func UnmarshalUint(v string) uint {
i, _ := strconv.ParseUint(v, 10, 64)
return uint(i)
}
Result:
Running tool: C:\Go\bin\go.exe test -benchmem -run=^$ myBench/main -bench .
goos: windows
goarch: amd64
pkg: myBench/main
BenchmarkUnmarshalInt-8 99994166 11.7 ns/op 0 B/op 0 allocs/op
BenchmarkUnmarshalUint-8 54550413 21.0 ns/op 0 B/op 0 allocs/op
Is it possible that the second (uint) is almost twice as slow as the first (int)?
Yes, it's possible. strconv.Atoi has a fast path when the input string length is less than 19 (or 10 if int is 32 bit). This allows it to be a lot faster because it doesn't need to check for overflow.
If you change your test number to "1234567890123456789" (assuming 64 bit int), then your int benchmark is slightly slower than the uint benchmark because the fast path can't be used. On my machine, it takes 37.6 ns/op for the signed version vs 31.5 ns/op for the unsigned version.
Here's the modified benchmark code (note I added a variable that sums up the parsed results, just in case the compiler got clever and optimized it away).
package main
import (
"fmt"
"strconv"
"testing"
)
const X = "1234567890123456789"
func BenchmarkUnmarshalInt(b *testing.B) {
var T int
for i := 0; i < b.N; i++ {
T += UnmarshalInt(X)
}
fmt.Println(T)
}
func BenchmarkUnmarshalUint(b *testing.B) {
var T uint
for i := 0; i < b.N; i++ {
T += UnmarshalUint(X)
}
fmt.Println(T)
}
func UnmarshalInt(v string) int {
i, _ := strconv.Atoi(v)
return i
}
func UnmarshalUint(v string) uint {
i, _ := strconv.ParseUint(v, 10, 64)
return uint(i)
}
For reference, the code for strconv.Atoi in the standard library is currently as follows:
func Atoi(s string) (int, error) {
const fnAtoi = "Atoi"
sLen := len(s)
if intSize == 32 && (0 < sLen && sLen < 10) ||
intSize == 64 && (0 < sLen && sLen < 19) {
// Fast path for small integers that fit int type.
s0 := s
if s[0] == '-' || s[0] == '+' {
s = s[1:]
if len(s) < 1 {
return 0, &NumError{fnAtoi, s0, ErrSyntax}
}
}
n := 0
for _, ch := range []byte(s) {
ch -= '0'
if ch > 9 {
return 0, &NumError{fnAtoi, s0, ErrSyntax}
}
n = n*10 + int(ch)
}
if s0[0] == '-' {
n = -n
}
return n, nil
}
// Slow path for invalid, big, or underscored integers.
i64, err := ParseInt(s, 10, 0)
if nerr, ok := err.(*NumError); ok {
nerr.Func = fnAtoi
}
return int(i64), err
}
I'm looking for the most efficient way to tell whether a byte slice is a float.
This is to be done on huge datasets, so performance is key.
Tried approaches:
strconv.ParseFloat
regexp.Match
CheckNumber - home rolled function using IsNumber + looking at whether the byte slice contains a ..
func CheckNumber(p []byte) bool {
r := string(p)
sep := 0
for _, b := range r {
if unicode.IsNumber(b) {
continue
}
if b == rune('.') {
if sep > 0 {
return false
}
sep++
continue
}
return false
}
return true
}
The benchmark code:
func BenchmarkFloatStrconv(b *testing.B) {
p := []byte("15.34234234234")
for i := 0; i < b.N; i++ {
_, err := strconv.ParseFloat(string(p), 64)
if err != nil {
log.Fatalf("NaN")
}
}
}
func BenchmarkFloatRegex(b *testing.B) {
p := []byte("15.34234234234")
r := `[-+]?[0-9]*\.?[0-9]`
c, _ := regexp.Compile(r)
for i := 0; i < b.N; i++ {
ok := c.Match(p)
if !ok {
log.Fatalf("NaN")
}
}
}
func BenchmarkCheckNumber(b *testing.B) {
p := []byte("15.34234234234")
for i := 0; i < b.N; i++ {
ok := CheckNumber(p)
if !ok {
log.Fatalf("NaN")
}
}
}
Benchmark results:
BenchmarkFloatStrconv-8 20000000 85.8 ns/op 16 B/op 1 allocs/op
BenchmarkFloatRegex-8 5000000 252 ns/op 0 B/op 0 allocs/op
BenchmarkCheckNumber-8 20000000 64.3 ns/op 0 B/op 0 allocs/op
Am I doing the different solutions fairness?
Are there better solutions?
Edit: thanks to pointers from Adrian and icza, this avoids converting to strings/runes
func CheckNumberNoStringConvert(r []byte) bool {
sep := 0
for i := range r {
if r[i] >= 48 && r[i] <= 57 {
continue
}
if r[i] == 46 {
if sep > 0 {
return false
}
sep++
continue
}
return false
}
return true
}
and performs quite well ;-)
BenchmarkCheckNumberNoStringConvert-8 200000000 8.55 ns/op 0 B/op 0 allocs/op
For a simple real (floating-point) number (no scientific or engineering floating-point format, no group separators),
func IsReal(n []byte) bool {
if len(n) > 0 && n[0] == '-' {
n = n[1:]
}
if len(n) == 0 {
return false
}
var point bool
for _, c := range n {
if '0' <= c && c <= '9' {
continue
}
if c == '.' && len(n) > 1 && !point {
point = true
continue
}
return false
}
return true
}
Benchmark:
$ go test -run=! -bench=. -benchmem -cpu=1 real_test.go
goos: linux
goarch: amd64
BenchmarkIsReal 100000000 20.8 ns/op 0 B/op 0 allocs/op
BenchmarkFloatStrconv 20000000 101 ns/op 16 B/op 1 allocs/op
BenchmarkFloatRegex 5000000 284 ns/op 0 B/op 0 allocs/op
BenchmarkCheckNumber 20000000 73.0 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 7.380s
real_test.go:
package main
import (
"log"
"regexp"
"strconv"
"testing"
"unicode"
)
func IsReal(n []byte) bool {
if len(n) > 0 && n[0] == '-' {
n = n[1:]
}
if len(n) == 0 {
return false
}
var point bool
for _, c := range n {
if '0' <= c && c <= '9' {
continue
}
if c == '.' && len(n) > 1 && !point {
point = true
continue
}
return false
}
return true
}
func BenchmarkIsReal(b *testing.B) {
p := []byte("15.34234234234")
for i := 0; i < b.N; i++ {
ok := IsReal(p)
if !ok {
log.Fatalf("NaN")
}
}
}
func CheckNumber(p []byte) bool {
r := string(p)
sep := 0
for _, b := range r {
if unicode.IsNumber(b) {
continue
}
if b == rune('.') {
if sep > 0 {
return false
}
sep++
continue
}
return false
}
return true
}
func BenchmarkFloatStrconv(b *testing.B) {
p := []byte("15.34234234234")
for i := 0; i < b.N; i++ {
_, err := strconv.ParseFloat(string(p), 64)
if err != nil {
log.Fatalf("NaN")
}
}
}
func BenchmarkFloatRegex(b *testing.B) {
p := []byte("15.34234234234")
r := `[-+]?[0-9]*\.?[0-9]`
c, _ := regexp.Compile(r)
for i := 0; i < b.N; i++ {
ok := c.Match(p)
if !ok {
log.Fatalf("NaN")
}
}
}
func BenchmarkCheckNumber(b *testing.B) {
p := []byte("15.34234234234")
for i := 0; i < b.N; i++ {
ok := CheckNumber(p)
if !ok {
log.Fatalf("NaN")
}
}
}
I took upon it as a challenge for myself to rewrite this as some kind of state machine synthesizing the collective input from everyone here :)
func Validate(b []byte) bool {
for i := range b {
switch {
case b[i] >= '0' && b[i] <= '9':
continue
case b[i] == '.':
if len(b) == 1 {
return false
}
if len(b) > i {
return fractional(b[i+1:])
}
return true
case i == 0 && b[i] == '-':
if len(b) == 1 {
return false
}
continue
default:
return false
}
}
return true
}
func fractional(b []byte) bool {
for i := range b {
switch {
case b[i] >= '0' && b[i] <= '9':
continue
case b[i] == 'e' || b[i] == 'E':
if len(b[:i]) == 0 {
return false
}
if len(b) > i+1 {
return scientific(b[i+1:])
}
return false
default:
return false
}
}
return true
}
func scientific(b []byte) bool {
for i := range b {
switch {
case b[i] >= '0' && b[i] <= '9':
continue
case i == 0 && b[i] == '-':
if len(b) == 1 {
return false
}
continue
default:
return false
}
}
return true
}
It seems to work on a few different number formats:
type v struct {
Input []byte
Expected bool
}
func TestPermutations(t *testing.T) {
b := []v{
v{[]byte("123.456"), true},
v{[]byte("123"), true},
v{[]byte("123."), true},
v{[]byte(".123"), true},
v{[]byte("12.1e12"), true},
v{[]byte("12.1e-12"), true},
v{[]byte("-123.456"), true},
v{[]byte("-123"), true},
v{[]byte("-123."), true},
v{[]byte("-.123"), true},
v{[]byte("-12.1e12"), true},
v{[]byte("-12.1e-12"), true},
v{[]byte(".1e-12"), true},
v{[]byte(".e-12"), false},
v{[]byte(".e"), false},
v{[]byte("e"), false},
v{[]byte("abcdef"), false},
v{[]byte("-"), false},
v{[]byte("."), false},
}
for _, test := range b {
ok := Validate(test.Input)
if ok != test.Expected {
t.Errorf("could not handle case %s", test.Input)
}
}
}
and perform quite well on the original benchmark:
BenchmarkValidate-8 100000000 13.0 ns/op 0 B/op 0 allocs/op
Benchmark code:
func BenchmarkValidate(b *testing.B) {
p := []byte("15.1234567890")
for i := 0; i < b.N; i++ {
ok := Validate(p)
if !ok {
log.Fatalf("problem")
}
}
}
I am trying to calculate the multiplication result of a few digits which are part of a long digits string. Here is my code:
package main
import (
"fmt"
"strconv"
)
func main() {
inputNum := "73167176531330624919225119"
mult := getMult(3, inputNum)
fmt.Printf("Mult = %d", mult)
}
func getMult(startIndex int, inputNum string) int {
mult := 0
for i := 0; i < 10; i++ {
digit, err := strconv.Atoi(string(inputNum[startIndex+i]))
if err != nil {
mult *= int(digit)
} else {
fmt.Printf("Error converting %s to int : %s\n", string(inputNum[startIndex+i]), err.Error())
}
}
return mult
}
The result I want to get is 6*7*1*7*6*5*3*1*3*3 = 238140
But I an getting a runtime error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x20 pc=0x40130e]
goroutine 1 [running]:
main.getMult(0x3, 0x534d40, 0x1a, 0x4d2701)
test.go:25 +0x17e
main.main()
test.go:10 +0x55
exit status 2
There are a couple problems...
First, you need to start mult at 1, otherwise you will just continually multiply by 0 and always get 0.
Secondly you have the logic for your err check flipped. It should be if err == nil
This seems to do what you want:
func getMult(startIndex int, inputNum string) int {
mult := 1
for i := 0; i < 10; i++ {
digit, err := strconv.Atoi(string(inputNum[startIndex+i]))
if err == nil {
mult *= int(digit)
} else {
fmt.Println(err)
}
}
return mult
}
The error you were getting was because you were trying to print err.Error() when err itself was nil (due to the logical flip of != and ==)
your code will work with fixing these two typos:
change mult := 0 to mult := 1
and change err != nil to err == nil like this:
package main
import (
"fmt"
"strconv"
)
func main() {
inputNum := "73167176531330624919225119"
mult := getMult(3, inputNum)
fmt.Printf("Mult = %d", mult)
}
func getMult(startIndex int, inputNum string) int {
mult := 1
for i := 0; i < 10; i++ {
digit, err := strconv.Atoi(string(inputNum[startIndex+i]))
if err == nil {
mult *= int(digit)
} else {
fmt.Printf("Error converting %s to int : %s\n", string(inputNum[startIndex+i]), err.Error())
}
}
return mult
}
also you may use inputNum[3:13] to get new string from index 3 with length 10,
and you may use int(v - '0') to convert one character to integer number,
then use for range like this:
package main
import "fmt"
func main() {
inputNum := "73167176531330624919225119"
mult := getMult(inputNum[3:13])
fmt.Printf("Mult = %d \n", mult) // Mult = 238140
}
func getMult(str string) int {
m := 1
for _, v := range str {
if v >= '0' && v <= '9' {
m *= int(v - '0')
} else {
fmt.Printf("Error converting %q to int\n", v)
break
}
}
return m
}
output:
Mult = 238140
I am trying to find a solution to check for equality in 2 slices. Unfortanely, the answers I have found require values in the slice to be in the same order. For example, http://play.golang.org/p/yV0q1_u3xR evaluates equality to false.
I want a solution that lets []string{"a","b","c"} == []string{"b","a","c"} evaluate to true.
MORE EXAMPLES
[]string{"a","a","c"} == []string{"c","a","c"} >>> false
[]string{"z","z","x"} == []string{"x","z","z"} >>> true
Here is an alternate solution, though perhaps a bit verbose:
func sameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
}
// create a map of string -> int
diff := make(map[string]int, len(x))
for _, _x := range x {
// 0 value for int is 0, so just increment a counter for the string
diff[_x]++
}
for _, _y := range y {
// If the string _y is not in diff bail out early
if _, ok := diff[_y]; !ok {
return false
}
diff[_y] -= 1
if diff[_y] == 0 {
delete(diff, _y)
}
}
return len(diff) == 0
}
Try it on the Go Playground
You can use cmp.Diff together with cmpopts.SortSlices:
less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
Here is a full example that runs on the Go Playground:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func main() {
x := []string{"a", "b", "c"}
y := []string{"a", "c", "b"}
less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
fmt.Println(equalIgnoreOrder) // prints "true"
}
The other answers have better time complexity O(N) vs (O(N log(N)), that are in my answer, also my solution will take up more memory if elements in the slices are repeated frequently, but I wanted to add it because I think this is the most straight forward way to do it:
package main
import (
"fmt"
"sort"
"reflect"
)
func array_sorted_equal(a, b []string) bool {
if len(a) != len(b) {return false }
a_copy := make([]string, len(a))
b_copy := make([]string, len(b))
copy(a_copy, a)
copy(b_copy, b)
sort.Strings(a_copy)
sort.Strings(b_copy)
return reflect.DeepEqual(a_copy, b_copy)
}
func main() {
a := []string {"a", "a", "c"}
b := []string {"c", "a", "c"}
c := []string {"z","z","x"}
d := []string {"x","z","z"}
fmt.Println( array_sorted_equal(a, b))
fmt.Println( array_sorted_equal(c, d))
}
Result:
false
true
I would think the easiest way would be to map the elements in each array/slice to their number of occurrences, then compare the maps:
func main() {
x := []string{"a","b","c"}
y := []string{"c","b","a"}
xMap := make(map[string]int)
yMap := make(map[string]int)
for _, xElem := range x {
xMap[xElem]++
}
for _, yElem := range y {
yMap[yElem]++
}
for xMapKey, xMapVal := range xMap {
if yMap[xMapKey] != xMapVal {
return false
}
}
return true
}
You'll need to add some additional due dilligence, like short circuiting if your arrays/slices contain elements of different types or are of different length.
Generalizing the code of testify ElementsMatch, solution to compare any kind of objects (in the example []map[string]string):
https://play.golang.org/p/xUS2ngrUWUl
Like adrianlzt wrote in his answer, an implementation of assert.ElementsMatch from testify can be used to achieve that. But how about reusing actual testify module instead of copying that code when all you need is a bool result of the comparison? The implementation in testify is intended for tests code and usually takes testing.T argument.
It turns out that ElementsMatch can be quite easily used outside of testing code. All it takes is a dummy implementation of an interface with ErrorF method:
type dummyt struct{}
func (t dummyt) Errorf(string, ...interface{}) {}
func elementsMatch(listA, listB interface{}) bool {
return assert.ElementsMatch(dummyt{}, listA, listB)
}
Or test it on The Go Playground, which I've adapted from the adrianlzt's example.
Since I haven't got enough reputation to comment, I have to post yet another answer with a bit better code readability:
func AssertSameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
}
itemAppearsTimes := make(map[string]int, len(x))
for _, i := range x {
itemAppearsTimes[i]++
}
for _, i := range y {
if _, ok := itemAppearsTimes[i]; !ok {
return false
}
itemAppearsTimes[i]--
if itemAppearsTimes[i] == 0 {
delete(itemAppearsTimes, i)
}
}
if len(itemAppearsTimes) == 0 {
return true
}
return false
}
The logic is the same as in this answer
I know its been answered but still I would like to add my answer. By following code here stretchr/testify we can have something like
func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
Now lets talk about performance of this solution compared to map based ones. Well it really depends on the size of the lists which you are comparing, If size of list is large (I would say greater than 20) then map approach is better else this would be sufficent.
Well on Go PlayGround it shows 0s always, but run this on local system and you can see the difference in time taken as size of list increases
So the solution I propose is, adding map based comparision from above solution
func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
if aLen > 20 {
return elementsMatchByMap(listA, listB)
}else{
return elementsMatchByLoop(listA, listB)
}
}
func elementsMatchByLoop(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
func elementsMatchByMap(x, y []string) (string, bool) {
// create a map of string -> int
diff := make(map[string]int, len(x))
for _, _x := range x {
// 0 value for int is 0, so just increment a counter for the string
diff[_x]++
}
for _, _y := range y {
// If the string _y is not in diff bail out early
if _, ok := diff[_y]; !ok {
return fmt.Sprintf(" %v is not present in list b", _y), false
}
diff[_y] -= 1
if diff[_y] == 0 {
delete(diff, _y)
}
}
if len(diff) == 0 {
return "", true
}
return "", false
}
I am just wondering if there is any beautiful way to iterate over two strings at the same time:
var ascii_runes = []rune(string_1)
var shifted_runes = []rune(string_2)
for i := 0; i < len(string_1); i++ {
fmt.Println(string(ascii_runes[i]) + string(shifted_runes[i]))
}
Not sure IIUC, but for example:
package main
import (
"fmt"
)
var (
ascii = []rune("string1")
shifted = []rune("STRING!")
)
func main() {
for i, v := range ascii {
fmt.Printf("%c%c\n", v, shifted[i])
}
}
Also here: http://play.golang.org/p/2ruvLFg_qe
Output:
sS
tT
rR
iI
nN
gG
1!
For example,
package main
import "fmt"
func main() {
var runes_1, runes_2 = []rune("string_1"), []rune("string_2")
for i := 0; i < len(runes_1) && i < len(runes_2); i++ {
fmt.Println(string(runes_1[i]) + string(runes_2[i]))
}
}
Output:
ss
tt
rr
ii
nn
gg
__
12
Not particularly beautiful, but an efficient way is to use strings.NewReader and its ReadRune method.
func Less(s1, s2 string) bool {
rdr1 := strings.NewReader(s1)
rdr2 := strings.NewReader(s2)
for {
rune1, _, err1 := rdr1.ReadRune()
rune2, _, err2 := rdr2.ReadRune()
if err2 == io.EOF { return false }
if err1 == io.EOF { return true }
if rune1 != rune2 { return rune1 < rune2 }
}
}