From febea43708b75bb27426e66d831f62ba9b21125c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 11 Jun 2026 19:12:17 -0600 Subject: [PATCH] refactor: move shouldMergeObjects from rawpaths to heuristics --- tools/jsontypes/heuristics.go | 34 ++++++++++++++++++++++++++++++++++ tools/jsontypes/rawpaths.go | 32 -------------------------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/tools/jsontypes/heuristics.go b/tools/jsontypes/heuristics.go index bfa2664..d944ab2 100644 --- a/tools/jsontypes/heuristics.go +++ b/tools/jsontypes/heuristics.go @@ -382,6 +382,40 @@ func capitalize(s string) string { } // singularize does a naive singularization for common English plurals. +// shouldMergeObjects decides whether a pool of objects should be treated as +// one struct type with optional fields, rather than multiple distinct types. +// +// Heuristic: if ≥ 2 keys appear in more than half the objects, the objects +// likely share a common shape with optional fields. This catches the common +// case of API responses where every record has the same core fields but some +// records have extra ones (e.g. pagination results). +func shouldMergeObjects(objects []map[string]any) bool { + n := len(objects) + if n < 2 { + return false + } + + // Count how many objects contain each key. + keyCount := make(map[string]int) + for _, obj := range objects { + for k := range obj { + keyCount[k]++ + } + } + + // Count keys that appear in a majority of objects. + threshold := n / 2 + highFreq := 0 + for _, count := range keyCount { + if count > threshold { + highFreq++ + } + } + + // Two or more high-frequency keys means the objects share a common core. + return highFreq >= 2 +} + func singularize(s string) string { // Uncountable / already-singular words ending in -s. switch strings.ToLower(s) { diff --git a/tools/jsontypes/rawpaths.go b/tools/jsontypes/rawpaths.go index 0eacd2b..9795de5 100644 --- a/tools/jsontypes/rawpaths.go +++ b/tools/jsontypes/rawpaths.go @@ -370,36 +370,4 @@ func joinPath(prefix, field string) string { return prefix + "." + field } -// shouldMergeObjects decides whether a pool of objects should be treated as -// one struct type with optional fields, rather than multiple distinct types. -// -// Heuristic: if ≥ 2 keys appear in more than half the objects, the objects -// likely share a common shape with optional fields. This catches the common -// case of API responses where every record has the same core fields but some -// records have extra ones (e.g. pagination results). -func shouldMergeObjects(objects []map[string]any) bool { - n := len(objects) - if n < 2 { - return false - } - // Count how many objects contain each key. - keyCount := make(map[string]int) - for _, obj := range objects { - for k := range obj { - keyCount[k]++ - } - } - - // Count keys that appear in a majority of objects. - threshold := n / 2 - highFreq := 0 - for _, count := range keyCount { - if count > threshold { - highFreq++ - } - } - - // Two or more high-frequency keys means the objects share a common core. - return highFreq >= 2 -}