I have a Grid class which is wrapper over 2d array of Cell objects. I would like this class to implement Iterable<Cell> interface in order to use it in loops and iterate directly overall cells. Is there a simple way to do that? Does Kotlin support yield return style iterators? My current solution is quite verbose:
override fun iterator() = object : Iterator<Cell> {
val currentOuter = grid.iterator() // grid is object of Array<Array<Cell>>
var currentInner = if (currentOuter.hasNext()) currentOuter.next().iterator() else arrayOf<Cell>().iterator()
override fun next(): Cell {
if (!hasNext()) {
throw NoSuchElementException()
}
return if (currentInner.hasNext()) {
currentInner.next()
} else {
currentInner = currentOuter.next().iterator()
currentInner.next()
}
}
override fun hasNext(): Boolean {
return currentInner.hasNext() || currentOuter.hasNext()
}
}
Does Kotlin support yield return style iterators?
Yes, it does, through the feature of coroutines. Here's a self-contained example:
data class Cell(val d: Int)
val grid: Array<Array<Cell>> = arrayOf(arrayOf(Cell(1), Cell(2)), arrayOf(Cell(3), Cell(4)))
fun cellSequence() = buildSequence {
grid.forEach { it.forEach { yield(it) } }
}
fun main(args: Array<String>) {
cellSequence().forEach { println(it) }
}
Although this particular problem could have been simply solved with just a flatMap, the presented code can serve as a template to write any kind of procedural-looking code, for example:
fun complexCellSequence() = buildSequence {
yield(Cell(-1))
if (grid.size <= 2) {
yield(Cell(2))
}
for (row in grid) {
if (row.contains(Cell(1))) {
yield(Cell(1))
} else {
yield(Cell(12))
row.forEach { yield(it) }
}
}
}
This would be quite nontrivial to rewrite without coroutines.
A very simple solution would be something like this:
val grid: Array<Array<Cell>> = ...
override fun iterator() : Iterator<Cell> = grid.flatMap { it.asList() }.iterator()
Related
I have a Kotlin data class that has an arg that can either be an Object or Array. Is there a way to de-serialize a string into this class and not care if not an Array but somehow get it into an array of one?
data class Game(var name:List<NameItem>)
data class NameItem(var title: String, var id: Int)
data can come back as both ways a single object or an array of objects( I have no control over the data as it is 3rd party data.
jsonString = "{"game":{"name":{"title":"GameName","id":22}}}"
jsonString = "{"game":{"name":[{"title":"GameName","id":22},{"title":"GameName2","id":23}]}}"
game: Game? = Gson().fromJson(jsonString Game::class.java)
You have to write a custom JsonDeserializer. Both or your class should have the same parent class. Then, write a custom JsonDeserializer for this specific type.
For example:
sealed class GameParent() {
data class Game(val name: String): GameParent()
data class GameList(val games: List<Game>): GameParent()
}
class GameDeserializer : JsonDeserializer<GameParent> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): GameParent {
with(json) {
if(isJsonObject) {
// parse as Game
}
if(isJsonArray) {
// parse as GameList
}
}
}
}
Then, in your GsonBuilder you have to register this custom JsonDeserializer:
gsonBuilder.registerTypeAdapter(GameParent::class.java, GameDeserializer());
Now, whenever your Gson expect GameParent will use registered deserializer.
my suggestions for solving your task
my solution if name is object, replace it with arrays
data class Game(var name:List<NameItem> )
data class NameItem(var title: String, var id: Int)
fun main(args: Array<String>) {
var json = "{\"game\":{\"name\":[{\"title\":\"game 1\",\"id\":1},{\"title\":\"game 2\",\"id\":2}]}}"
println(useJsonParser(json)) //Game(name=[NameItem(title=game 1, id=1), NameItem(title=game 2, id=2)])
json = "{\"game\":{\"name\":[{\"title\":\"game 1\",\"id\":1}]}}"
println(useJsonParser(json)) //Game(name=[NameItem(title=game 1, id=1), NameItem(title=game 2, id=2)])
json = "{\"game\":{\"name\":{\"title\":\"game 1\",\"id\":1}}}" // not array
println(useJsonParser(json)) //Game(name=[NameItem(title=game 1, id=1)])
}
version 1 -- created and registry adapter link #Cililing
fun useJsonParser(json: String): Game? {
val gson = GsonBuilder().registerTypeAdapter(Game::class.java, GameDeserializer()).create()
return gson.fromJson(json, Game::class.java)
}
class GameDeserializer : JsonDeserializer<Game?> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Game? {
val gameJson = json!!.asJsonObject.get("game")
if (gameJson.isJsonObject) {
val jsonName = gameJson.asJsonObject["name"]
val list = if (jsonName.isJsonObject) {
arrayOf(Gson().fromJson(jsonName, NameItem::class.java))
} else {
val fromJson = Gson().fromJson(jsonName, Array<NameItem>::class.java)
fromJson
}.toList()
return Game(list)
}
return null
}
}
version 2 -- manipulating the response
fun useJsonParser(json:String):Game?{
val jsonObject = JsonParser().parse(json).asJsonObject.get("game")
if(jsonObject.asJsonObject["name"].isJsonObject){
val jsonName = jsonObject.asJsonObject["name"].asJsonObject
val array = JsonArray()
array.add(jsonName)
jsonObject.asJsonObject.add("name", array) // rewrite origin JSON
}
return Gson().fromJson(jsonObject, Game::class.java)
}
vesrion 3 -- add adapter TypeToken>()
fun useJsonParser(json: String): Game? {
val type = object : TypeToken<MutableList<NameItem>>() {}.type
val gson = GsonBuilder().registerTypeAdapter(type, NameItemDeserializer()).create()
return gson.fromJson(JsonParser().parse(json).asJsonObject.get("game"), Game::class.java)
}
class NameItemDeserializer : JsonDeserializer<List<NameItem>?> {
override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext?): List<NameItem>? {
with(json){
return if(isJsonObject){
arrayListOf(Gson().fromJson(this,NameItem::class.java))
}else{
Gson().fromJson(this,Array<NameItem>::class.java).toList()
}
}
}
}
I have an array of viewController class names like (ViewController class 1, 2 ,3 4)
let classArray = [VCclass1, VCclass2, VCclass3, VCclass4]
I want to check the object belongs to any of the class mentioned in class array like
if obj.isKind(of:(Any of the classArray elements) ) {
//do something
} else {
//execute else condition
}
How can I write "if condition" here?
Well, you're looking to see if your array contains a member according to the type of obj. So, contains(where:) is a perfect fit:
if classes.contains(where: { type(of: obj) == $0 }) {
//do something
} else {
//execute else condition
}
You can verify that in a simple way.
import Foundation
import UIKit
class A: UIViewController {}
class B: UIViewController {}
class C: UIViewController {}
let array:[UIViewController.Type] = [A.self,B.self,C.self]
let obj = A()
print(array.contains(where: { obj.isKind(of: $0) }))
Output should be true. You can run this code in Playground
However I would recommend switch for that purpose. In more complicated scenario you will want to know which class is given object or so.
switch obj {
case is A:
print("A")
case is B:
print("B")
case is C:
print("C")
default:
print("none")
}
Try this:
extension NSObject {
func isKind(of classes: [AnyClass]) -> Bool {
for aClass in classes {
if self.isKind(of: aClass) {
return true
}
}
return false
}
}
let classes = [UIViewController.self]
let obj = UIViewController()
if (obj.isKind(of: classes)) {
//do something
}
or the uglier, less OOP way...
var isKindOfClass = false
for aClass in classes {
if obj.isKind(of: aClass) {
isKindOfClass = true
break
}
}
if isKindOfClass {
//do something
}
I have a class for a linked list declared like this:
class LinkedNode<T> {
let data: T
var next: LinkedNode<T>?
func traverseList(process: (LinkedNode<T>) -> ()) { ... }
}
What I want to do is extend Array to have an initialiser that converts my LinkedNode class to an array of linked nodes. I tried this:
extension Array where Element == LinkedNode<T> {
init(node: LinkedNode<T>)
{
var result = [LinkedNode<T>]()
traverseList { result.append($0) }
return result
}
}
But that gives errors that T is undeclared. I have tried taking it out and doing other things, but nothing has worked.
I was able to get the same results with a method on the LinkedNode class:
func array() -> [LinkedNode<T>]
{
var result = [LinkedNode<T>]()
traverseList { result.append($0) }
return result
}
But I would prefer an array initialiser since so many other collection types have that initialiser.
You can declare a generic parameter in initializer definition.
extension Array {
init<T>(node: LinkedNode<T>)
where Element == LinkedNode<T>
{
var result = [LinkedNode<T>]()
node.traverseList { result.append($0) }
self = result
}
}
I am trying to make a simple minesweeper game in Scala and I am trying to make a method that calls a nested method for randomly placing mines into the grid.
I am using the ofDim method on the Array to make it multi-dimensional which works great, until I specify it as the methods return type. The error I get is:
type ofDim is not a member of Object Array
The code is as follows:
class GridBuilder(x:Int, y:Int, mines:Int) {
def generateGrid: Array.ofDim[String](x, y) = {
def placeMines(mineLocations: Array.ofDim[String](x, y)): Array.ofDim[String](x, y) = {
val xcoord = Random.nextInt(x)
val ycoord = Random.nextInt(y)
if (mineLocations.count(_ == "Bomb") == mines)
mineLocations
else if (mineLocations(xcoord) (ycoord) contains "Mine")
placeMines(mineLocations)
else
placeMines(mineLocations(xcoord)(ycoord) = "Mine")
}
placeMines(new Array.ofDim[String](x, y))
}
}
I haven't found anything about returning multi-dimensional arrays anywhere. Is this possible in Scala? What am I missing here?
Array.ofDim is a method, not a type,
If you look at how it's implemented, it's a bunch of overloaded methods, each having different return type. In your case Array[Array[String]].
I threw together a quick little example so you can compare what you end up with. It is by no means perfect, and doesn't cover invalid user input, or game over etc, but here it is anyway.
import scala.annotation.tailrec
import scala.util.Random
sealed trait GridValue
case object Bomb extends GridValue
case object Empty extends GridValue
case object Checked extends GridValue
case class Grid(private var grid: List[List[GridValue]]){
def click(x: Int, y: Int): String = {
grid(x)(y) match {
case Bomb => "BOOM! You lose!"
case Empty =>
grid = grid.updated(x, grid(x).updated(y, Checked))
"No bomb! Good Job"
case Checked => "You've already clicked here!"
}
}
}
object Grid{
def buildGrid(x: Int, y: Int, mines: Int): Grid = {
#tailrec
def placeMines(grid: List[List[GridValue]] = List.fill(x)(List.fill(y)(Empty)), minesRemaining: Int = mines): List[List[GridValue]] = {
if(minesRemaining == 0) {
grid
} else {
val xcoord = Random.nextInt(x)
val ycoord = Random.nextInt(y)
grid(xcoord)(ycoord) match {
case Bomb => placeMines(grid, minesRemaining)
case Empty => placeMines(grid.updated(xcoord,grid(xcoord).updated(ycoord, Bomb)), minesRemaining - 1)
}
}
}
Grid(placeMines())
}
}
//Start game: val game = Grid.buildGrid(100, 100, 5)
//Click: game.click(10, 10)
Unfortunately in Actionscript, it seems like support for the Vector class isn't fully there yet. There are some scenarios where I need to convert a Vector into an array (creating an ArrayCollection for example). I thought this would do the trick:
var myVector:Vector.<MyType> = new Vector.<MyType>();
var newArray:Array = new Array(myVector);
Apparently this just creates an array where the first index of the array contains the full Vector object. Is this my only option:
var newArray:Array = new Array(myVector);
for each(var item:MyType in myVector)
{
newArray.push(item);
}
I feel like that clutters up the code a lot and I need to do this in a lot of places. The Vector class doesn't implement any kind of interface, so as far as I can tell I can't create a generic function to convert to an array. Is there any way to do this without adding this mess every time I want to convert a Vector to an array?
There's no easy/fast way to do it, the best solution is to use an utility class like this one:
package {
public class VectorUtil {
public static function toArray(obj:Object):Array {
if (!obj) {
return [];
} else if (obj is Array) {
return obj as Array;
} else if (obj is Vector.<*>) {
var array:Array = new Array(obj.length);
for (var i:int = 0; i < obj.length; i++) {
array[i] = obj[i];
}
return array;
} else {
return [obj];
}
}
}
}
Then you just have to update your code to something like this:
var myArray:Array = VectorUtil.toArray(myVector);
Paul at Work found a better way to do it.
var newArray:Array = [].concat(myVector);