Go Golang

25 set, 2025

Função recursiva em Go para acessar valores em mapas aninhados

Publicidade

GO | Recentemente, recebi um retorno de uma API que continha um JSON com uma estrutura complexa, grande e aninhada. Eu precisava acessar um valor específico dentro dessa estrutura, mas o caminho para esse valor poderia variar.

A ideia então foi criar uma função recursiva que recebesse uma lista com o caminho (path) para o valor desejado e a estrutura de dados (um mapa aninhado) e retornasse o valor correspondente, se encontrado.

Acredito que existem maneiras mais espertas de fazer isso, mas essa abordagem recursiva funcionou bem para o meu caso.

package main

import (
	"fmt"
)

func GetFromMap(parts []string, current any) (any, bool) {
	fmt.Printf("parts: %v, current: %v\n", parts, current)

	if len(parts) == 0 {
		return current, true
	}

	m, ok := current.(map[string]any)
	if !ok { // not a map, cannot descend
		return nil, false
	}

	key := parts[0]
	next, exists := m[key]
	if !exists {
		return nil, false
	}

	return GetFromMap(parts[1:], next)
}

func MustGetFromMap(path []string, data any) any {
	val, ok := GetFromMap(path, data)
	if !ok {
		panic(fmt.Sprintf("path %v not found in map", path))
	}
	return val
}

E aqui está um exemplo de como você pode testar essa função:

package main

import "testing"

func TestGetFromMap(t *testing.T) {
	data := map[string]any{
		"not_a_map": "just a string",
		"a": map[string]any{
			"b": map[string]any{
				"c": 42,
			},
			"d": "hello",
		},
		"x": 3.14,
	}

	tests := []struct {
		path          []string
		expectedVal   any
		expectedFound bool
	}{
		{
			path:          []string{"a", "b", "c"},
			expectedVal:   42,
			expectedFound: true,
		},
		{
			path:          []string{"a", "d"},
			expectedVal:   "hello",
			expectedFound: true,
		},
		{
			path:          []string{"x"},
			expectedVal:   3.14,
			expectedFound: true,
		},
		{
			path:          []string{"a", "not_found"},
			expectedVal:   nil,
			expectedFound: false,
		},
	}

	for _, tt := range tests {
		val, found := GetFromMap(tt.path, data)
		if found != tt.expectedFound {
			t.Errorf("GetFromMap(%v) found = %v; want %v", tt.path, found, tt.expectedFound)
		}
		if val != tt.expectedVal {
			t.Errorf("GetFromMap(%v) = %v; want %v", tt.path, val, tt.expectedVal)
		}
	}
}

E, por favor, quando for criar uma API, lembre-se do seu colega programador que vai consumir a API. Estruturas simples e planas que não mudam de formato com frequência são melhores. Aliás, simples sempre é melhor.