tom
tom

Reputation: 9

Getting a slice of integers from a string integer in Go?

I want to convert a string representing and integer into a slice containing each integer that made up the initial string.

Playground link

I've tried using the Atoi() function in strconv, it it gives "cannot use toCheck[i] (type byte) as type string in argument to strconv.Atoi". I've also tried int() conversion, but it's giving me what I believe are ASCII values - I know that I can just subtract 48, but that seems a little hacky.

package main

import (
    "fmt"
)

func main() {

toCheck := "987654321"

    var toSum []int
    for i := len(toCheck) - 2; i >= 0; i = i-2 {
        toSum = append(toSum, int(toCheck[i]))
    }

    fmt.Println(toSum)

    // expected 2 4 6 8
    // actual 50 52 54 56
}

Expected output: 2 4 6 Actual: 50 52 54 56

Upvotes: 1

Views: 2167

Answers (3)

peterSO
peterSO

Reputation: 166569

For example,

package main

import (
    "fmt"
)

func main() {
    toCheck := "987654321"
    fmt.Println(toCheck)
    toSum := make([]int, 0, len(toCheck)/2)
    for i := len(toCheck) - 2; i >= 0; i -= 2 {
        c := toCheck[i]
        if c >= '0' && c <= '9' {
            toSum = append(toSum, int(c-'0'))
        }
    }
    fmt.Println(len(toSum), cap(toSum), toSum)
}

Playground: https://play.golang.org/p/wtIgqEKj-Bk

Output:

987654321
4 4 [2 4 6 8]

This is Go. Code should be reasonably efficient. For example,

$ go test sum_test.go -bench=. -benchmem
BenchmarkPeterSO-8         50000000     24.5 ns/op    32 B/op    1 allocs/op
BenchmarkTom-8             20000000     77.6 ns/op    56 B/op    3 allocs/op
BenchmarkUser10753492-8    20000000     79.0 ns/op    56 B/op    3 allocs/op
BenchmarkGrissom-8         20000000    108 ns/op      56 B/op    3 allocs/op
$

sum_test.go:

package main

import (
    "strconv"
    "testing"
)

// https://play.golang.org/p/wtIgqEKj-Bk
func BenchmarkPeterSO(b *testing.B) {
    toCheck := "987654321"
    for N := 0; N < b.N; N++ {
        toSum := make([]int, 0, len(toCheck)/2)
        for i := len(toCheck) - 2; i >= 0; i -= 2 {
            c := toCheck[i]
            if c >= '0' && c <= '9' {
                toSum = append(toSum, int(c-'0'))
            }
        }
    }
}

// https://play.golang.org/p/KgQrbesy5rT
func BenchmarkTom(b *testing.B) {
    toCheck := "987654321"
    for N := 0; N < b.N; N++ {
        var toSum []int
        for i := len(toCheck) - 2; i >= 0; i = i - 2 {
            toSum = append(toSum, int(toCheck[i]))
        }
    }
}

func ByteToInt(c byte) (int, bool) {
    if c >= '0' && c <= '9' {
        return int(c - '0'), true
    } else {
        return 0, false
    }
}

// https://play.golang.org/p/MRqtgY0ugZY
func BenchmarkUser10753492(b *testing.B) {
    toCheck := "987654321"
    for N := 0; N < b.N; N++ {
        var toSum []int
        for i := len(toCheck) - 2; i >= 0; i = i - 2 {
            var digit, _ = ByteToInt(toCheck[i])
            toSum = append(toSum, digit)
        }
    }
}

// https://play.golang.org/p/kNbQVn8GJ9R
func BenchmarkGrissom(b *testing.B) {
    toCheck := "987654321"
    for N := 0; N < b.N; N++ {
        var toSum []int
        for i := len(toCheck) - 2; i >= 0; i = i - 2 {
            v, _ := strconv.Atoi(string(toCheck[i]))
            toSum = append(toSum, v)
        }
    }
}

Upvotes: 1

user10753492
user10753492

Reputation: 778

but it's giving me what I believe are ASCII values - I know that I can just subtract 48, but that seems a little hacky.

It's not hacky. You have a byte representing an ascii digit and you want to get its value as an integer from 0-9. That's how you do it. That's also how converting a string to an integer works in general. You convert each individual byte to a number using this method.

The UTF-8 representation for '0' is not going to change anytime soon. Of course, instead of subtracting 48 you can subtract '0'.

To make it more general purpose, maybe you should also return a boolean indicating whether the byte can be converted or not.

func ByteToInt(c byte) (int, bool) {
    if c >= '0' && c <= '9' {
        return int(c - '0'), true
    } else {
        return 0, false
    }
}

You can use this in your code sample and it would return your expected result:

package main

import (
    "fmt"
)

func ByteToInt(c byte) (int, bool) {
    if c >= '0' && c <='9' {
        return int(c - '0'), true
    } else {
        return 0, false
    }
}

func main() {

toCheck := "987654321"

    var toSum []int
    for i := len(toCheck) - 2; i >= 0; i = i-2 {
        var digit, _ = ByteToInt(toCheck[i])
        toSum = append(toSum, digit)
    }

    fmt.Println(toSum)

}

https://play.golang.org/p/MRqtgY0ugZY

The output is what you expect: [2 4 6 8]

Upvotes: 2

Grissom
Grissom

Reputation: 66

use strconv package to do the conversion

  for i := len(toCheck) - 2; i >= 0; i = i-2 {
        v,_ := strconv.Atoi(string(toCheck[i]))
        toSum = append(toSum, v)
    }

strconv.Atoi is shortcut for strconv.ParseInt(s,10,0).

finish in your playground https://play.golang.org/p/kNbQVn8GJ9R

Upvotes: 0

Related Questions