I have multiple different data classes in kotlin and they are saved in extra lists with the exact same length. Now I want to combine them into a result list which contains them as a map with the same length (1000 entries). I tried it with a for loop and a prefilled list of maps, but that looks a bit messy to me, so I asked myself if there is a cleaner way of doing this, especially with kotlin.
data class One(
val a: Double,
val b: Double,
var c: Double
)
data class Two(
val d: Double,
val e: Double,
)
fun main() {
val oneList: List<One> = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList: List<Two> = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
val results: List<MutableMap<String,Double>> = (0..1).map{ mutableMapOf() }
for(i in 0 .. 1){
results[i]["a"] = oneList[i].a
results[i]["b"] = oneList[i].b
results[i]["c"] = oneList[i].c
results[i]["d"] = twoList[i].d
results[i]["e"] = twoList[i].e
}
}
The problem is, that I have about 10 classes with around 2-3 members in each object, whereby the code would be about 30+ lines... The result should look like this:
[
{
a: 1.0,
b: 0.0,
c: 2.0,
d: 5.0,
e: 2.0
},
{
a: 3.0,
b: 5.0,
c: 10.0,
d: 7.0,
e: 1.0
}
]
You could use .zip in order to link the 2 lists and then directly create the list using .map and a simple mapOf() to create the map.
val results = oneList.zip(twoList).map { (one, two) ->
mapOf("a" to one.a, "b" to one.b, "c" to one.c, "d" to two.d, "e" to two.e)
}
As for actually doing this for a lot of classes ... not 100% sure. You could use reflexion maybe, but that is not something you use in an actual program usually. Another way would be to create a function in each of the classes that would give a map and then just add those maps together in the .map from above. That way, the code would look a bit cleaner. Something along the lines of:
data class One(
val a: Double,
val b: Double,
var c: Double
){
fun toMap(): Map<String, Double> {
return mapOf("a" to a, "b" to b, "c" to c)
}
}
//.....
val results = oneList.zip(twoList).map { (one, two) -> one.toMap() + two.toMap() }
But you'll soon notice that zip doesn't work with more than 2 lists. I would suggest implementing something like this: Zip multiple lists SO. But that as well won't work, since your classes are of a different type. What I would do is create a abstract class with a toMap() fun, and then all the classes that you need there can inherit it. It would look something like this:
abstract class MotherClass(){
abstract fun toMap(): Map<String, Double>
}
data class One(
val a: Double,
val b: Double,
var c: Double
):MotherClass() {
override fun toMap(): Map<String, Double> {
return mapOf("a" to a, "b" to b, "c" to c)
}
}
// ...
val results = zip(oneList, twoList /*, threeList, etc */).map { list -> list.map { it.toMap() } }
So at the end of the day, you want a String representation of a property name, mapped to its value? I think you have two choices there:
use reflection to fetch all the member names, filter on the types you want (e.g. only the Doubles), so your Strings are derived directly from the class at runtime
define a String somewhere that acts as a label for each property, and try to tie it as closely to the class as you can, so it's easy to maintain. Because it's not derived from the property itself, you'll have to keep the property name and its label in sync - there's no inherent connection between the two things that could automate it
You're already doing the latter in your code - you're using hardcoded arbitrary labels like "a" and "b" when building your results map, and that's where you're connecting a label with a property (e.g. results[i]["a"] = oneList[i].a). So here's another way you could approach that!
Kotlin playground
interface Mappable {
// allows us to declare classes as having the property map we're using
abstract val propertyMap: Map<String, Double>
}
data class One(
val a: Double,
val b: Double,
var c: Double
) : Mappable {
// you have to do this association somewhere - may as well be in the class itself
// you could also use reflection to build this map automatically
override val propertyMap = mapOf("a" to a, "b" to b, "c" to c)
}
data class Two(
val d: Double,
val e: Double,
) : Mappable {
override val propertyMap = mapOf("d" to d, "e" to e)
}
fun main() {
val oneList = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
combine(oneList, twoList).forEach(::println)
}
fun combine(vararg lists: List<Mappable>): List<Map<String, Double>> {
// lists could be different lengths, so we go until one of them runs out
val length = lists.minOf { it.size }
return (0 until length).map { index ->
// create a combined map for each index
mutableMapOf<String, Double>().apply {
// visit each list at this index, grabbing the property map from each object
// and adding its contents to the combined map we're building
lists.map { it.elementAt(index).propertyMap }.forEach(this::putAll)
}
}
}
>> {a=1.0, b=0.0, c=2.0, d=5.0, e=2.0}
{a=3.0, b=5.0, c=10.0, d=7.0, e=1.0}
The problem really is that artificial connection you're introducing between a property, and a label you explicitly define for it - which you have to ensure you maintain, and if you don't, you got bugs. I think that's unavoidable unless you use reflection.
Also if by any chance this data is originally coming from something arbitrary like JSON (where the problem of validation and labelling a property is earlier in the chain) you could take a look at Kotlin's map delegate and see if that's a better fit for this approach
import kotlin.reflect.full.declaredMemberProperties
data class One(val a: Double, val b: Double, var c: Double)
data class Two(val d: Double, val e: Double)
val oneList: List<One> = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList: List<Two> = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
fun compounded(vararg lists: List<Any>): List<Map<String, Double>> {
return lists
.mapIndexed { index, _ ->
lists
.flatMap {
it[index]::class.declaredMemberProperties.map { prop ->
mapOf(prop.name to prop.call(it[index]) as Double)
}
}
.fold(mapOf()) { acc, map ->
mutableMapOf<String, Double>().apply { putAll(acc + map) }
}
}
}
val result = compounded(oneList, twoList)
I'm using fscheck to write some unite tests and I would like to narrow down the range of decimal automatically generated and that regardless of the parameter I'm passing. What I mean by that is that let's say I have the types below:
decimal
DecimalHolder
Nested records containing decimal fields
DU with cases with decimal fields
Without having something to define an arbitrary for each single type, just that down the line in the generation if there a decimal it must say be between 0 and 300,000.
module Tests
open Xunit
open FsCheck.Xunit
open Swensen.Unquote
let addDecimals a b: decimal =
a + b
[<Property>]
let ``test adding two decimals`` a b =
let actual = addDecimals a b
let expected = a + b
test<# actual = expected #>
type DecimalHolder =
{ Value: decimal }
let addDecimalHolders a b =
{ Value = a.Value + b.Value }
[<Property>]
let ``test adding two decimal holders`` a b =
let actual = addDecimalHolders a b
let expected = { Value = a.Value + b.Value }
test<# actual = expected #>
type DecimalStuff =
| Value of decimal
| Holder of DecimalHolder
| Holders of DecimalHolder list
// Whatever
etc.
How can I achieve that?
Ok actually the Arbitrary definition works recursively across parameters types was enough:
module Tests
open Xunit
open FsCheck.Xunit
open Swensen.Unquote
type NotBigPositiveDecimalArbitrary =
static member NotBigPositiveDecimal() =
Gen.choose (1, 500)
|> Gen.map (fun x -> decimal x)
|> Arb.fromGen
let addDecimals a b: decimal =
a + b
[<Property(Arbitrary = [| typeof<NotBigPositiveDecimalArbitrary> |])>]
let ``test adding two decimals`` a b =
let actual = addDecimals a b
let expected = a + b
test<# actual = expected #>
type DecimalHolder =
{ Value: decimal }
let addDecimalHolders a b =
{ Value = a.Value + b.Value }
[<Property(Arbitrary = [| typeof<NotBigPositiveDecimalArbitrary> |])>]
let ``test adding two decimal holders`` a b =
let actual = addDecimalHolders a b
let expected = { Value = a.Value + b.Value }
test<# actual = expected #>
I'm trying to convert the following ES6 script to bucklescript and I cannot for the life of me figure out how to create a "closure" in bucklescript
import {Socket, Presence} from "phoenix"
let socket = new Socket("/socket", {
params: {user_id: window.location.search.split("=")[1]}
})
let channel = socket.channel("room:lobby", {})
let presence = new Presence(channel)
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
response += `<br>${id} (count: ${count})</br>`
})
document.querySelector("main[role=main]").innerHTML = response
}
socket.connect()
presence.onSync(() => renderOnlineUsers(presence))
channel.join()
the part I cant figure out specifically is let response = "" (or var in this case as bucklescript always uses vars):
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
response += `<br>${id} (count: ${count})</br>`
})
document.querySelector("main[role=main]").innerHTML = response
}
the closest I've gotten so far excludes the result declaration
...
...
let onPresenceSync ev =
let result = "" in
let listFunc = [%raw begin
{|
(id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
result += `${id} (count: ${count})\n`
}
|}
end
] in
let _ =
presence |. listPresence (listFunc) in
[%raw {| console.log(result) |} ]
...
...
compiles to:
function onPresenceSync(ev) {
var listFunc = (
(id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
result += `${id} (count: ${count})\n`
}
);
presence.list(listFunc);
return ( console.log(result) );
}
result is removed as an optimization beacuse it is considered unused. It is generally not a good idea to use raw code that depends on code generated by BuckleScript, as there's quite a few surprises you can encounter in the generated code.
It is also not a great idea to mutate variables considered immutable by the compiler, as it will perform optimizations based on the assumption that the value will never change.
The simplest fix here is to just replace [%raw {| console.log(result) |} ] with Js.log result, but it might be enlightening to see how listFunc could be written in OCaml:
let onPresenceSync ev =
let result = ref "" in
let listFunc = fun [#bs] id item ->
let count = Js.Array.length item##meta in
result := {j|$id (count: $count)\n|j}
in
let _ = presence |. (listPresence listFunc) in
Js.log !result
Note that result is now a ref cell, which is how you specify a mutable variable in OCaml. ref cells are updated using := and the value it contains is retrieved using !. Note also the [#bs] annotation used to specify an uncurried function needed on functions passed to external higher-order functions. And the string interpolation syntax used: {j| ... |j}
I ve got the following class and I want to write some Spec test cases, but I am really new to it and I don't know how to start. My class do loke like this:
class Board{
val array = Array.fill(7)(Array.fill(6)(None:Option[Coin]))
def move(x:Int, coin:Coin) {
val y = array(x).indexOf(None)
require(y >= 0)
array(x)(y) = Some(coin)
}
def apply(x: Int, y: Int):Option[Coin] =
if (0 <= x && x < 7 && 0 <= y && y < 6) array(x)(y)
else None
def winner: Option[Coin] = winner(Cross).orElse(winner(Naught))
private def winner(coin:Coin):Option[Coin] = {
val rows = (0 until 6).map(y => (0 until 7).map( x => apply(x,y)))
val cols = (0 until 7).map(x => (0 until 6).map( y => apply(x,y)))
val dia1 = (0 until 4).map(x => (0 until 6).map( y => apply(x+y,y)))
val dia2 = (3 until 7).map(x => (0 until 6).map( y => apply(x-y,y)))
val slice = List.fill(4)(Some(coin))
if((rows ++ cols ++ dia1 ++ dia2).exists(_.containsSlice(slice)))
Some(coin)
else None
}
override def toString = {
val string = new StringBuilder
for(y <- 5 to 0 by -1; x <- 0 to 6){
string.append(apply(x, y).getOrElse("_"))
if (x == 6) string.append ("\n")
else string.append("|")
}
string.append("0 1 2 3 4 5 6\n").toString
}
}
Thank you!
I can only second Daniel's suggestion, because you'll end up with a more practical API by using TDD.
I also think that your application could be nicely tested with a mix of specs2 and ScalaCheck. Here the draft of a Specification to get you started:
import org.specs2._
import org.scalacheck.{Arbitrary, Gen}
class TestSpec extends Specification with ScalaCheck { def is =
"moving a coin in a column moves the coin to the nearest empty slot" ! e1^
"a coin wins if" ^
"a row contains 4 consecutive coins" ! e2^
"a column contains 4 consecutive coins" ! e3^
"a diagonal contains 4 consecutive coins" ! e4^
end
def e1 = check { (b: Board, x: Int, c: Coin) =>
try { b.move(x, c) } catch { case e => () }
// either there was a coin before somewhere in that column
// or there is now after the move
(0 until 6).exists(y => b(x, y).isDefined)
}
def e2 = pending
def e3 = pending
def e4 = pending
/**
* Random data for Coins, x position and Board
*/
implicit def arbitraryCoin: Arbitrary[Coin] = Arbitrary { Gen.oneOf(Cross, Naught) }
implicit def arbitraryXPosition: Arbitrary[Int] = Arbitrary { Gen.choose(0, 6) }
implicit def arbitraryBoardMove: Arbitrary[(Int, Coin)] = Arbitrary {
for {
coin <- arbitraryCoin.arbitrary
x <- arbitraryXPosition.arbitrary
} yield (x, coin)
}
implicit def arbitraryBoard: Arbitrary[Board] = Arbitrary {
for {
moves <- Gen.listOf1(arbitraryBoardMove.arbitrary)
} yield {
val board = new Board
moves.foreach { case (x, coin) =>
try { board.move(x, coin) } catch { case e => () }}
board
}
}
}
object Cross extends Coin {
override def toString = "x"
}
object Naught extends Coin {
override def toString = "o"
}
sealed trait Coin
The e1 property I've implemented is not the real thing because it doesn't really check that we moved the coin to the nearest empty slot, which is what your code and your API suggests. You will also want to change the generated data so that the Boards are generated with an alternation of x and o. That should be a great way to learn how to use ScalaCheck!
I suggest you throw all that code out -- well, save it somewhere, but start from zero using TDD.
The Specs2 site has plenty examples of how to write tests, but use TDD -- test driven design -- to do it. Adding tests after the fact is suboptimal, to say the least.
So, think of the most simple case you want to handle of the most simple feature, write a test for that, see it fail, write the code to fix it. Refactor if necessary, and repeat for the next most simple case.
If you want help with how to do TDD in general, I heartily endorse the videos about TDD available on Clean Coders. At the very least, watch the second part where Bob Martin writes a whole class TDD-style, from design to end.
If you know how to do testing in general but are confused about Scala or Specs, please be much more specific about what your questions are.
I am working on ANLTR to support type checking. I am in trouble at some point. I will try to explain it with an example grammar, suppose that I have the following:
#members {
private java.util.HashMap<String, String> mapping = new java.util.HashMap<String, String>();
}
var_dec
: type_specifiers d=dec_list? SEMICOLON
{
mapping.put($d.ids.get(0).toString(), $type_specifiers.type_name);
System.out.println("identext = " + $d.ids.get(0).toString() + " - " + $type_specifiers.type_name);
};
type_specifiers returns [String type_name]
: 'int' { $type_name = "int";}
| 'float' {$type_name = "float"; }
;
dec_list returns [List ids]
: ( a += ID brackets*) (COMMA ( a += ID brackets* ) )*
{$ids = $a;}
;
brackets : LBRACKET (ICONST | ID) RBRACKET;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
LBRACKET : '[';
RBRACKET : ']';
In rule dec_list, you will see that I am returning List with ids. However, in var_dec when I try to put the first element of the list (I am using only get(0) just to see the return value from dec_list rule, I can iterate it later, that's not my point) into mapping I get a whole string like
[#4,6:6='a',<17>,1:6]
for an input
int a, b;
What I am trying to do is to get text of each ID, in this case a and b in the list of index 0 and 1, respectively.
Does anyone have any idea?
The += operator creates a List of Tokens, not just the text these Tokens match. You'll need to initialize the List in the #init{...} block of the rule and add the inner-text of the tokens yourself.
Also, you don't need to do this:
type_specifiers returns [String type_name]
: 'int' { $type_name = "int";}
| ...
;
simply access type_specifiers's text attribute from the rule you use it in and remove the returns statement, like this:
var_dec
: t=type_specifiers ... {System.out.println($t.text);}
;
type_specifiers
: 'int'
| ...
;
Try something like this:
grammar T;
var_dec
: type dec_list? ';'
{
System.out.println("type = " + $type.text);
System.out.println("ids = " + $dec_list.ids);
}
;
type
: Int
| Float
;
dec_list returns [List ids]
#init{$ids = new ArrayList();}
: a=ID {$ids.add($a.text);} (',' b=ID {$ids.add($b.text);})*
;
Int : 'int';
Float : 'float';
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
Space : ' ' {skip();};
which will print the following to the console:
type = int
ids = [a, b, foo]
If you run the following class:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
TLexer lexer = new TLexer(new ANTLRStringStream("int a, b, foo;"));
TParser parser = new TParser(new CommonTokenStream(lexer));
parser.var_dec();
}
}