Created
October 2, 2023 16:44
-
-
Save igor-ramazanov/4768c7e7794862eae30615bb3e93b27b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> 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