Skip to content

Instantly share code, notes, and snippets.

@igor-ramazanov
Created October 2, 2023 16:44
Show Gist options
  • Save igor-ramazanov/4768c7e7794862eae30615bb3e93b27b to your computer and use it in GitHub Desktop.
Save igor-ramazanov/4768c7e7794862eae30615bb3e93b27b to your computer and use it in GitHub Desktop.
//> using scala 3.3.1
//> using options -Wnonunit-statement, -Wunused:all, -Wvalue-discard, -Yno-experimental, -Ysafe-init, -deprecation, -feature, -new-syntax, -unchecked,
//> using platform native
//> using nativeGc none
//> using nativeLto thin
//> using nativeMode release-full
//> using nativeEmbedResources false
//> using dep io.circe::circe-parser::0.14.6
//> using dep com.monovore::decline::2.4.1
import cats.syntax.show.*
import cats.Show
import com.monovore.decline.CommandApp
import com.monovore.decline.Opts
import io.circe.Json
import java.nio.file.Path
enum JType:
case Boolean
case String
case Null
case Number
object JType:
given Show[JType] = Show.show {
case Boolean => "bool"
case String => "string"
case Null => "null"
case Number => "number"
}
opaque type JPath = String
extension (path: JPath) def +(s: String): JPath = path + s
object JPath:
given Show[JPath] = Show.show(identity)
def apply(s: String): JPath = s
object Main
extends CommandApp(
name = "json-histogram",
header = "Print the histogram stats for each key.",
helpFlag = true,
main = Opts.argument[Path]("JSON file").map(Main.main),
version = "0.1.0-SNAPSHOT",
):
def main(path: Path): Unit =
val content = new String(java.nio.file.Files.readAllBytes(path))
val json = io.circe.parser.parse(content).toOption.get
val result = process(json)
print(result)
private def process(json: Json): Map[JPath, Map[JType, Int]] =
def go(
acc: Map[(JPath, JType), Int],
path: JPath,
json: Json,
): Map[(JPath, JType), Int] =
def scalar(jType: JType) = acc
.updatedWith(path -> jType)(_.map(_ + 1).orElse(Some(1)))
json.fold(
jsonNull = scalar(JType.Null),
jsonBoolean = _ => scalar(JType.Boolean),
jsonNumber = _ => scalar(JType.Number),
jsonString = _ => scalar(JType.String),
jsonObject = _
.toList
.foldLeft(acc) { case (a, (k, j)) => go(a, path + ("." + k), j) },
jsonArray = _
.zipWithIndex
.foldLeft(acc) { case (a, (j, i)) => go(a, path + ".[]", j) },
)
go(Map.empty, JPath(""), json)
.groupBy { case ((path, _), _) => path }
.view
.mapValues(_.map { case ((_, jType), counter) => jType -> counter })
.toMap
private def print(result: Map[JPath, Map[JType, Int]]): Unit =
val msg = result
.map { case (path, counters) =>
val value = counters
.toList
.sortBy((_, counter) => counter)
.reverse
.map((jType, counter) => show"$jType:$counter")
.mkString(", ")
show"$path = $value"
}
.toList
.sorted
.mkString("\n")
println(msg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment