From 32cd7a61f17ff55f3abba10a05dbe777ef81a5e6 Mon Sep 17 00:00:00 2001 From: kageru Date: Tue, 26 Nov 2019 22:02:39 +0100 Subject: [PATCH] Implement grouping, mapping, and sorting on Nodes --- src/main/kotlin/moe/kageru/spektacles/Node.kt | 71 +++++++++++++++++++ .../kotlin/moe/kageru/spektacles/Parser.kt | 5 +- .../moe/kageru/spektacles/Spektacles.kt | 61 ++++++---------- 3 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 src/main/kotlin/moe/kageru/spektacles/Node.kt diff --git a/src/main/kotlin/moe/kageru/spektacles/Node.kt b/src/main/kotlin/moe/kageru/spektacles/Node.kt new file mode 100644 index 0000000..f9cf891 --- /dev/null +++ b/src/main/kotlin/moe/kageru/spektacles/Node.kt @@ -0,0 +1,71 @@ +package moe.kageru.spektacles + +import kotlin.collections.LinkedHashMap + + +// maybe try this instead? +// https://arrow-kt.io/docs/recursion/intro/ +sealed class Node(open val value: Any) { + class Leaf(override val value: List) : Node(value) + class Split(val children: Map>) : Node(children) + class Value(override val value: T) : Node(value) + + fun map(op: (T) -> R): Node { + return when (this) { + is Leaf -> Leaf(value.map(op)) + is Value -> Value(op(value)) + is Split -> Split(children.mapValues { it.value.map(op) }) + } + } + + fun groupBy(keyGenerator: (T) -> K): Node { + return when (this) { + is Leaf -> Split(value.groupBy(keyGenerator).mapValues { Leaf(it.value) }) + is Value -> this + is Split -> Split(children.mapValues { it.value.groupBy(keyGenerator) }) + } + } + + override fun toString() = asString() + + private fun asString(depth: Int = 0): String { + return when (this) { + is Leaf -> value.joinToString(indent(depth), prefix = "\n${indent(depth)}") + is Value -> value.toString() + is Split -> children.entries.joinToString("") { "\n${indent(depth)}${it.key}: ${it.value.asString(depth + 1)}" } + } + } + + fun countLeafs(): Node { + return when (this) { + is Leaf -> Value(value.size) + is Value -> Value(1) + is Split -> Split(children.mapValues { it.value.countLeafs() }) + } + } + + private fun indent(depth: Int) = (0..(depth * 2)).joinToString("") { " " } +} + +fun Node.sort(): Node { + return when (this) { + is Node.Leaf -> this + is Node.Value -> this + is Node.Split -> Node.Split(this.children.sortByValues().mapValues { it.value.sort() }) + } +} + +private fun Map>.sortByValues(): Map> { + val sortedEntries = entries.sortedBy { + when (val v = it.value) { + is Node.Value -> v.value + is Node.Leaf -> v.value.size + is Node.Split -> Int.MAX_VALUE + } + } + return LinkedHashMap>().apply { + for ((key, value) in sortedEntries) { + this[key] = value + } + } +} diff --git a/src/main/kotlin/moe/kageru/spektacles/Parser.kt b/src/main/kotlin/moe/kageru/spektacles/Parser.kt index d469174..147fe0e 100644 --- a/src/main/kotlin/moe/kageru/spektacles/Parser.kt +++ b/src/main/kotlin/moe/kageru/spektacles/Parser.kt @@ -13,9 +13,12 @@ class ParsedLine(val time: LocalDateTime, ) { override fun toString() = originalMessage + fun getMention(): Option = mentionRegex.find(message).toOption().map { it.value.toLowerCase() } + companion object { // 18 04 26 19:40:47 Hi private val lineRegex = Regex("""(?\d\d)\s(?\d\d)\s(?\d\d)\s(?\d\d):(?\d\d):(?\d\d)\s(<(?[~&@%+])?(?\w+)>\t)?(?.*)""") + private val mentionRegex = Regex("(?<=@)\\w+") fun parse(line: String): ParsedLine? = lineRegex .matchEntire(line) @@ -52,5 +55,5 @@ typealias ParsedLines = Sequence fun Lines.parse(): ParsedLines { return map { ParsedLine.parse(it) } - .filterNotNull() + .filterNotNull() } diff --git a/src/main/kotlin/moe/kageru/spektacles/Spektacles.kt b/src/main/kotlin/moe/kageru/spektacles/Spektacles.kt index b001883..126ccc0 100644 --- a/src/main/kotlin/moe/kageru/spektacles/Spektacles.kt +++ b/src/main/kotlin/moe/kageru/spektacles/Spektacles.kt @@ -1,11 +1,6 @@ package moe.kageru.spektacles -import arrow.Kind -import arrow.core.* -import arrow.core.extensions.mapk.functor.functor -import arrow.typeclasses.Functor import moe.kageru.spektacles.Node.Leaf -import java.time.Month import kotlin.system.measureTimeMillis fun main() { @@ -17,43 +12,27 @@ fun main() { fun spektacle() { IoHandler.readFile("test.log") - .parse() - .filterUsers("kageru_").toList().k() - .let, Node>(::Leaf) - //.grouped { it.time.year } - //.mapValues { it.value.groupBy { it.time.month } }.k() - //.ap(mapOf( + .parse() + .filterUsers("kageru_").toList() + .filter { it.getMention().nonEmpty() } + .toLeaf() + .groupBy { it.time.year.toString() } + .groupBy { it.time.monthValue.toString() } + .groupBy { it.getMention().orNull()!! } + .countLeafs() + .sort() + //.grouped { it.time.year } + //.mapValues { it.value.groupBy { it.time.month } }.k() + //.ap(mapOf( //1 to { lines: Map> -> lines }, //2 to {lines -> lines.filterKeys { it < Month.APRIL }} - //).k()) - //.let { it } - //.filterModes(UserMode.OP) - //.filterMonths(Month.APRIL) - //.heatMapByHour() - //.deepMap { it.message } - .let(::println) + //).k()) + //.let { it } + //.filterModes(UserMode.OP) + //.filterMonths(Month.APRIL) + //.heatMapByHour() + //.deepMap { it.message } + .let(::println) } -// https://arrow-kt.io/docs/recursion/intro/ - -sealed class Node { - class Leaf(val values: List): Node() - class Intersection(val children: Map>): Node() - - fun map(op: (T) -> R): Node { - return when (this) { - is Leaf -> Leaf(values.map(op)) - is Intersection<*, *> -> Intersection(children.mapValues { it.value.map(op)}) - } - } -} - -/* -private fun ListK.grouped(op: (A) -> K): ListK>> { - return this.groupBy(op).map { it.key toT it.value.k() }.k() -} - -private fun MapK.deepMap(AP: Functor>, op: (A) -> R): Kind { - val a = AP.run { this@deepMap.mapValues { op(it.value) } } -} - */ +private fun List.toLeaf(): Node = Leaf(this)