Kotlin foreach object - arrays

I have a object class,
object Examples {
const val E = 1
const val X = 2
const val A = 3
const val M = 4
const val P = 5
const val L = 6
}
if I use
Examples.E
it will reference the relevant const val, but how can I iterate over each val without calling them individually?

It looks like you want an enum:
enum class Example(val intVal: Int) {
E(1), X(2), A(3), M(4), P(5), L(6);
}
You can access specific values like Example.E.intVal. To get a collection of all the values, you can do Example.values() or enumValues<Example>()

Well, best way to do this is use Enum class.
But you also could implement Iterable interface on yours object. And after this you will be able to use forEach/map/fold/filter and other useful Iterable extension functions
object Examples : Iterable<Int> {
const val E = 1
const val X = 2
const val A = 3
const val M = 4
const val P = 5
const val L = 6
override fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
private var i = 1
override fun hasNext(): Boolean = i <= 6
override fun next(): Int {
return when (i++) {
1 -> E
2 -> X
3 -> A
4 -> M
5 -> P
6 -> L
else -> error("No such element exeption")
}
}
}
}
}
Examples.forEach {
println(it)
}

With Kotlin, you could use reflection to get all values inside an object.
However, did you consider using an Enum class? This seems like it would fit your use case better:
enum class Letter {
A,
B,
C
}
You can get all possible values for Letter using
Letter.values()
Or if what you want is to be able to reference repeated elements in order, you could use an array, or just a string, if the elements are always letters.
Also keep in mind you have two values called "E" defined, which wouldn't work.

Related

Initialize Array of non-optionals without using constructor

I am using an Array of non-optional values, and I want them to stay non-optional, but I can't use Array's default constructor because of problems described here.
Furthermore, the .also{} trick described in the linked won't work for me, because mine is not an array of some primitive type with its own special WhateverArray class.
Is there some Kotlin trick by which I can initialize my a below? Or must I resort to building some list and then converting it?
// please assume Stream<MyNonprimitiveType> magically gives me
// size() -> Int and
// next() -> MyNonprimitiveType
val stream : Stream<MyNonprimitiveType> = Stream<MyNonprimitiveType>()
val size : Int = stream.size()
val a : Array<MyNonprimitiveType> = ??? // use stream.next()
Here's a complete example doing what you want, without using a temporary list:
class Stream<T>(private val list: List<T>) {
val size = list.size;
private val it = list.iterator()
fun next(): T {
return it.next()
}
}
inline fun <reified T: Any> Stream<T>.toArray(): Array<T> {
val tmp: Array<T?> = arrayOfNulls(size)
for (i in 0 until size) {
tmp[i] = next()
}
return tmp as Array<T>
}
fun main() {
val stream : Stream<String> = Stream(listOf("a", "b"))
val a: Array<String> = stream.toArray()
println(Arrays.toString(a))
}

Casting between Swift array types without copy or allocations

I want to access an existing array of UInt64 as if it is an array of Int8. Key requirement is efficiency - I don't want to copy or reallocate the data, just have direct access. I don't want side effects (for example I want to be able to continue to use the uint64Array after this block of code has executed, was reading about rebinding having undefined side effects.)
I tried doing this with Swift 4.2:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBufferPointer() {
uint64Pointer in
uint64Pointer.withMemoryRebound(to: Int8.self) { // <- Error occurs here.
int8Pointer in
int8Pointer[0] = 1
int8Pointer[1] = 2
int8Pointer[2] = 3
int8Pointer[3] = 4
}
}
However I get a Fatal Error at runtime on the following line:
uint64Pointer.withMemoryRebound(to: Int8.self) {
Is this the right approach? If so, why am I getting the Fatal Error?
I think the problem is that you can't bind to a different type directly as per this note in the docs:
Only use this method to rebind the buffer’s memory to a type with the same size and stride as the currently bound Element type. To bind a region of memory to a type that is a different size, convert the buffer to a raw buffer and use the bindMemory(to:) method.
If bytes is what you're after then the quickest route is:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBytes { x in
x[0] = 1
x[1] = 2
x[3] = 3
x[4] = 4
}
If you have another type you'd like to use you can do it like this:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBufferPointer() {
uint64Pointer in
let x = UnsafeMutableRawBufferPointer(uint64Pointer).bindMemory(to: Int32.self)
x[0] = 1
x[1] = 2
x[3] = 3
x[4] = 4
}
Thanks to #brindy for solving this one. Here is an extension implementation that is as clean as I could get it.
The extension:
extension Array {
mutating func bindMutableMemoryTo<T,R>(_ type: T.Type, _ closure: (UnsafeMutableBufferPointer<T>) throws -> R) rethrows -> R {
return try self.withUnsafeMutableBytes() {
return try closure($0.bindMemory(to: type))
}
}
}
Usage:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.bindMutableMemoryTo(Int8.self) {
int8Pointer in
int8Pointer[0] = 1 // LSB of uint64Array[0]
int8Pointer[1] = 2
int8Pointer[2] = 3
int8Pointer[3] = 4 // MSB of uint64Array[0]
}

Mapping arrays to list of objects kotlin

I'm wondering about methods of mapping multiple arrays into one list of object.
I mean e.g. I have
val a = arrayOf("A1","A2","A3")
val b = arrayOf("B1","B2","B3")
and
data class SomeClass(val v1:String, val v2:String)
I want to parse it in elegant way to have list like that:
val list = listOf(SomeClass("A1","B1"),SomeClass("A2","B2"),SomeClass("A3","B3"))
I assume they are of the same length. The only way I thought of is:
val list = mutableListOf<SomeClass>()
for (i in a.indices)
array.add(SomeClass(a[i],b[i])
Is there a better, more elegant solution (maybe using Collecions.zip or Array.map)?
Try Array.zip and then map:
val list = a.zip(b)
.map { SomeClass(it.first, it.second) }
or if you like it more:
val list = a.zip(b)
.map { (a, b) -> SomeClass(a, b) }
Note that if both arrays differ in size, the additional values are ignored. Note also that this will create intermediate Pairs (which is the default transformation function of zip). Even though I like the explicit map more, #hotkeys solution regarding the overloaded method is more appropriate (you spare that hidden Pair-transformation):
val list = a.zip(b) { a, b -> SomeClass(a, b) }
And where the overloaded method probably shines, is when using references instead:
a.zip(b, ::SomeClass)
Which will work as long as you have a constructor matching the zipped arguments and doesn't work out of the box for the Pair (yet?).
Improving on #Roland's answer, you can use the zip overload that accepts a two-argument function for mapping the pairs immediately:
val result = a.zip(b) { x, y -> SomeClass(x, y) }
You can write some custom fun like this:
inline fun <T, R, E, V> Iterable<T>.zipThree(other1: Iterable<R>, other2: Iterable<E>, transform: (T, R, E) -> V): List<V> {
val first = iterator()
val second = other1.iterator()
val third = other2.iterator()
val list = ArrayList<V>()
while (first.hasNext() && second.hasNext()) {
list.add(transform(first.next(), second.next(), third.next()))
}
return list
}
And use this transform for getting List
val strings = listOf("1", "2")
val ints = listOf(1, 2)
val boolean = listOf(true, false)
val listYoutObjects = strings.zipThree(ints, boolean) { one, two, three -> YouObject(one, two, three) }

How to order an object by a parameter that is an array of numbers (Swift)

I want to order an object by a parameter. But this parameter is not a value but an array of values.
class MyObject:{
var arrayOfDoubles: [Double]
}
I´ve solved how to order the param arrayOfDoubles
self.arrayOfDoubles.sorted(by: >)
My problem now is how to order the array myObjects by the param arrayOfDoubles
myObjects: [MyObject]
I´ve tried this solution, but it only works when the param is a number, not an array of numbers
myObjects.sorted(by: { $0.arrayOfDoubles > $1.arrayOfDoubles })
This might not be exactly what you want, because I can't figure out what you want (and I'm not sure you do either). But in general your problems will be over if you define a custom struct and make it Equatable and Comparable. Then you can just sort using > or < directly, like this:
struct Pair : Comparable {
let ix1:Int
let ix2:Int
init(_ ix1: Int, _ ix2:Int) {
self.ix1 = ix1; self.ix2 = ix2
}
static func ==(lhs:Pair, rhs:Pair) -> Bool {
return lhs.ix1 == rhs.ix1 && lhs.ix2 == rhs.ix2
}
static func <(lhs:Pair, rhs:Pair) -> Bool {
return lhs.ix1 < rhs.ix1 && lhs.ix2 < rhs.ix2
}
}
let array1 = [Pair(1,3), Pair(10,11), Pair(0,1)]
let array2 = array1.sorted(by:>)
// [{ix1 10, ix2 11}, {ix1 1, ix2 3}, {ix1 0, ix2 1}]
As I say, that's only an example; tweak it so that it says what you mean (if you even know what you mean).
For instance, if this Pair is now to be a property of another object type Obj, that's trivial in just the same way:
struct Obj {
let pair : Pair
}
let array3 = [Obj(pair:Pair(1,3)), Obj(pair:Pair(10,11)), Obj(pair:Pair(0,1))]
let array4 = array3.sorted {$0.pair > $1.pair}
I believe that's the sort of language your question says you'd like to use...

How to check a generic type inside a Kotlin function?

I am using Kotlin to parse JSON. For example, I have this representation of a country: {"code":"US", "name":"United States of America"}. To produce a Country object from such a JSONObject, I have this function:
val produceCountry = fun (js: JSONObject) =
Country(js.getString("code"), js.getString("name"))
I can easily parse an array of Country with this function. Besides arrays of Country, however, I also have arrays of Cat, Car, Cart, CordlessPhone, etc. Each has their own produce* function transforming a JSONObject to a Kotlin object of that type. To generalize array parsing, I have this function:
fun <T> produceSetOf(array: JSONArray, element: (JSONObject) -> T): Set<T> {
val set = mutableSetOf<T>()
for (i in 0 until array.length())
set.add(element(array.getJSONObject(i)))
return set
}
So I can call produceSetOf(jsonArray, produceCountry) on encountering an array whose elements are of type Country. This works well on arrays of Cat, Car, Cart, CordlessPhone too.
Problem arises when I see an array of strings. Instead of array.getJSONObject(i), I have to use array.getString(i). In effect, I am thinking of introducing another parameterized type to the function above and have it make the call differently:
fun <S,T> produceSetOf(array: JSONArray, element: (S) -> T): Set<T> {
val set = mutableSetOf<T>()
for (i in 0 until array.length()) {
when (S) {
is String ->
set.add(element(array.getString(i)))
is JSONObject ->
set.add(element(array.getJSONObject(i)))
}
}
return set
}
Of course, Kotlin does not allow me to do that. Any suggestion how I could do that while maintaining the generality of produceSetOf() and without introducing another layer of abstraction (e.g. an element iterator, or a function transforming an index into String/JSONObject)?
Thank you.
Here is one possible solution using reified type parameters.
inline fun <reified S, T> produceSetOf(array: JsonArray, element: (S) -> T): Set<T> {
val set = mutableSetOf<T>()
for (i in 0 until array.size()) {
when (S::class) {
String::class -> set.add(element(array[i].string as S))
JsonObject::class -> set.add(element(array[i].obj as S))
}
}
return set
}
val stringArray = listOf("1", "2").toJsonArray()
val stringSet = produceSetOf<String, Int>(stringArray) { it.toInt() }
println(stringSet) // prints [1, 2]
val objArray = listOf(jsonObject("key" to "value"), jsonObject("key" to "other")).toJsonArray()
val objSet = produceSetOf<JsonObject, String>(objArray) { it["key"].string }
println(objSet) // print [value, other]
I used gson for the Json objects, since I didn't know where yours were from.
A possible shorter solution:
inline fun <reified S, T> produceSetOf(array: JsonArray, element: (S) -> T): Set<T> = array.map {
when (S::class) {
String::class -> element(it.string as S)
JsonObject::class -> element(it.obj as S)
else -> throw UnsupportedOperationException("${S::class.simpleName} is not supported")
}
}.toSet()

Resources