Skip to content

Instantly share code, notes, and snippets.

@Raphy42
Created August 26, 2018 15:55
Show Gist options
  • Save Raphy42/3c4479baae372e8bbec5f688fd38f7af to your computer and use it in GitHub Desktop.
Save Raphy42/3c4479baae372e8bbec5f688fd38f7af to your computer and use it in GitHub Desktop.
Flatten an arbitrary nested int array with go
package main
import (
"fmt"
"reflect"
)
// FlattenInt recursively flattens an arbitrary nested int array.
// Instead of defining an overly nested slice from the get-go (eg: [][][][][][][][]int)
// we are using an []interface{} and a type switch to resolve types at runtime.
// What is lost in speed is gained in readability and maintainability.
func FlattenInt(items []interface{}) ([]int64, error) {
array := make([]int64, 0)
for _, item := range items {
switch el := item.(type) {
case int64: // item is an integer
array = append(array, el)
case float64: // item might be an integer (eg: decoded from json.Unmarshal)
// we check if the float number can be converted to an integer
if el != float64(int64(el)) {
return array, fmt.Errorf("invalid type '%s'", reflect.TypeOf(el))
}
array = append(array, int64(el))
case []interface{}: // item is an array
result, err := FlattenInt(el)
if err != nil {
return array, err
}
array = append(array, result...)
default: // item is something else
return array, fmt.Errorf("invalid type '%s'", reflect.TypeOf(el))
}
}
return array, nil
}
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"encoding/json"
)
var (
// TestCases contains input to test flattening of a nested int array
testCases = []struct {
// a JSON serialized nested int array
Input []byte
// the expected int slice
Expected []int64
// the expected error (if there is one)
ExpectedError string
}{
{
Input: []byte(`[[1,2,[3]],4]`),
Expected: []int64{1, 2, 3, 4},
},
{
Input: []byte(`[1, 2, 3, [4, 5, 6, [7, 8], [9, 10]]]`),
Expected: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
{
Input: []byte(`[1, 2, 3, ["hello"], 4, 5]`),
ExpectedError: "invalid type 'string'",
},
{
Input: []byte(`[1, 2.42, 3]`),
ExpectedError: "invalid type 'float64'",
},
}
)
func TestFlattenInt(t *testing.T) {
a := assert.New(t)
for _, testCase := range testCases {
array := make([]interface{}, 0)
// we unmarshal the JSON array and pass its output to FlattenInt
// it allows us to easily manipulate nightmarish nested int arrays
err := json.Unmarshal(testCase.Input, &array)
a.NoError(err, "invalid input")
result, err := FlattenInt(array)
if testCase.ExpectedError != "" {
a.EqualError(err, testCase.ExpectedError)
} else {
a.Equal(testCase.Expected, result)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment