Implement grouping, mapping, and sorting on Nodes
This commit is contained in:
parent
fa66c1ddcf
commit
32cd7a61f1
|
@ -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<K, T : Any>(open val value: Any) {
|
||||||
|
class Leaf<K, T : Any>(override val value: List<T>) : Node<K, T>(value)
|
||||||
|
class Split<K, T : Any>(val children: Map<K, Node<K, T>>) : Node<K, T>(children)
|
||||||
|
class Value<K, T : Any>(override val value: T) : Node<K, T>(value)
|
||||||
|
|
||||||
|
fun <R : Any> map(op: (T) -> R): Node<K, R> {
|
||||||
|
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<K, T> {
|
||||||
|
return when (this) {
|
||||||
|
is Leaf -> Split(value.groupBy(keyGenerator).mapValues { Leaf<K, T>(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<K, Int> {
|
||||||
|
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 <K> Node<K, Int>.sort(): Node<K, Int> {
|
||||||
|
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 <K> Map<K, Node<K, Int>>.sortByValues(): Map<K, Node<K, Int>> {
|
||||||
|
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<K, Node<K, Int>>().apply {
|
||||||
|
for ((key, value) in sortedEntries) {
|
||||||
|
this[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,9 +13,12 @@ class ParsedLine(val time: LocalDateTime,
|
||||||
) {
|
) {
|
||||||
override fun toString() = originalMessage
|
override fun toString() = originalMessage
|
||||||
|
|
||||||
|
fun getMention(): Option<String> = mentionRegex.find(message).toOption().map { it.value.toLowerCase() }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// 18 04 26 19:40:47 <kageru_> Hi
|
// 18 04 26 19:40:47 <kageru_> Hi
|
||||||
private val lineRegex = Regex("""(?<year>\d\d)\s(?<month>\d\d)\s(?<day>\d\d)\s(?<hour>\d\d):(?<minute>\d\d):(?<second>\d\d)\s(<(?<mode>[~&@%+])?(?<author>\w+)>\t)?(?<message>.*)""")
|
private val lineRegex = Regex("""(?<year>\d\d)\s(?<month>\d\d)\s(?<day>\d\d)\s(?<hour>\d\d):(?<minute>\d\d):(?<second>\d\d)\s(<(?<mode>[~&@%+])?(?<author>\w+)>\t)?(?<message>.*)""")
|
||||||
|
private val mentionRegex = Regex("(?<=@)\\w+")
|
||||||
|
|
||||||
fun parse(line: String): ParsedLine? = lineRegex
|
fun parse(line: String): ParsedLine? = lineRegex
|
||||||
.matchEntire(line)
|
.matchEntire(line)
|
||||||
|
@ -52,5 +55,5 @@ typealias ParsedLines = Sequence<ParsedLine>
|
||||||
|
|
||||||
fun Lines.parse(): ParsedLines {
|
fun Lines.parse(): ParsedLines {
|
||||||
return map { ParsedLine.parse(it) }
|
return map { ParsedLine.parse(it) }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
package moe.kageru.spektacles
|
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 moe.kageru.spektacles.Node.Leaf
|
||||||
import java.time.Month
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
@ -17,43 +12,27 @@ fun main() {
|
||||||
|
|
||||||
fun spektacle() {
|
fun spektacle() {
|
||||||
IoHandler.readFile("test.log")
|
IoHandler.readFile("test.log")
|
||||||
.parse()
|
.parse()
|
||||||
.filterUsers("kageru_").toList().k()
|
.filterUsers("kageru_").toList()
|
||||||
.let<List<ParsedLine>, Node<ParsedLine>>(::Leaf)
|
.filter { it.getMention().nonEmpty() }
|
||||||
//.grouped { it.time.year }
|
.toLeaf<String, ParsedLine>()
|
||||||
//.mapValues { it.value.groupBy { it.time.month } }.k()
|
.groupBy { it.time.year.toString() }
|
||||||
//.ap(mapOf(
|
.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<Month, List<ParsedLine>> -> lines },
|
//1 to { lines: Map<Month, List<ParsedLine>> -> lines },
|
||||||
//2 to {lines -> lines.filterKeys { it < Month.APRIL }}
|
//2 to {lines -> lines.filterKeys { it < Month.APRIL }}
|
||||||
//).k())
|
//).k())
|
||||||
//.let { it }
|
//.let { it }
|
||||||
//.filterModes(UserMode.OP)
|
//.filterModes(UserMode.OP)
|
||||||
//.filterMonths(Month.APRIL)
|
//.filterMonths(Month.APRIL)
|
||||||
//.heatMapByHour()
|
//.heatMapByHour()
|
||||||
//.deepMap { it.message }
|
//.deepMap { it.message }
|
||||||
.let(::println)
|
.let(::println)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://arrow-kt.io/docs/recursion/intro/
|
private fun <K, T: Any> List<T>.toLeaf(): Node<K, T> = Leaf(this)
|
||||||
|
|
||||||
sealed class Node<T> {
|
|
||||||
class Leaf<T>(val values: List<T>): Node<T>()
|
|
||||||
class Intersection<K, T>(val children: Map<K, Node<T>>): Node<T>()
|
|
||||||
|
|
||||||
fun <K, R> map(op: (T) -> R): Node<R> {
|
|
||||||
return when (this) {
|
|
||||||
is Leaf -> Leaf(values.map(op))
|
|
||||||
is Intersection<*, *> -> Intersection(children.mapValues { it.value.map<K, R>(op)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private fun <A, K> ListK<A>.grouped(op: (A) -> K): ListK<Tuple2<K, ListK<A>>> {
|
|
||||||
return this.groupBy(op).map { it.key toT it.value.k() }.k()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <K, A, R> MapK<K, A>.deepMap(AP: Functor<Kind<ForMapK, K>>, op: (A) -> R): Kind<ForMapK, A> {
|
|
||||||
val a = AP.run { this@deepMap.mapValues { op(it.value) } }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user