Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concise and robust way to read a line of space-separated integers in Go

Tags:

arrays

parsing

go

I have been giving HackerRank a try where the problems often require reading lines of integers into arrays (slices).

For many of the problems, my parsing code ends up being larger than the algorithmic meat of the solution. For instance, that was the case in Sherlock and Array

Any ideas on how to concisely parse a space-separated line of integers into a slice? fmt.Scanf doesn't support slices and when using bufio I get long solutions.

Some requirements:

  • You can only use the standard library.
  • The solution should be concise, the shorter the better.
  • Error checks shouldn't be skipped. I know that the input is well defined in HackerRank and you should be able to cut corners, but please don't, it's bad practice.
  • It should be reasonably efficient.

NOTE: The parser should only consume a single line and not the full input.

like image 439
fons Avatar asked May 28 '16 11:05

fons


3 Answers

Well, I have done some hackerrank problems too, and here is what I came up with. Typically, problems start with the number of items in the array:

func main() {
    var N int
    fmt.Scanf("%d", &N)
    line := make([]int, N)
    for i, _ := range line {
        fmt.Scanf("%d", &line[i])
    }

    // Do something with the values
}
like image 97
T. Claverie Avatar answered Oct 06 '22 00:10

T. Claverie


// inputs space separated list of integers, outputs []int64
package main

import (
    "bufio"
    "fmt"
    "strconv"
    "strings"
)

func main() {
    fmt.Println(parse("100 200 300"))
}

func parse(i string) (o []int64) {
    // from https://golang.org/pkg/bufio/#example_Scanner_custom
    s := bufio.NewScanner(strings.NewReader(i))
    splitter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
        advance, token, err = bufio.ScanWords(data, atEOF)
        if err == nil && token != nil {
            x, err := strconv.ParseInt(string(token), 10, 32)
            if err != nil {
                panic(err)
            }
            o = append(o, x)
        }
        return
    }
    s.Split(splitter)
    for s.Scan() {
    }
    return o
}
like image 43
Plato Avatar answered Oct 05 '22 23:10

Plato


You can use fmt.Scanf, but you need to keep track of the values you're getting.

// a.go
package main

import (
    "fmt"
    "io"
)

func main() {
    var (
        next int
        nums []int
    )

    for {
        n, err := fmt.Scanf("%d", &next)
        if err == io.EOF {
            break
        }
        if err != nil {
            panic(err)
        }
        if n == 0 {
            break
        }

        nums = append(nums, next)
    }

    fmt.Printf("%#v\n", nums)
}

$ echo "4 8 15 16 23 42" | go run a.go
[]int{4, 8, 15, 16, 23, 42}
like image 27
noisypixy Avatar answered Oct 05 '22 22:10

noisypixy