Mapping arrays to list of objects kotlin - arrays

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) }

Related

Appending integers to an array in scala, but printing the array(even while using .mkSting("") doesn't show anything

I'm reading a text file and have the syntax set up correctly to do so. What I want to do now is append all the integers into an array, but when I try to use a print statement to check what's going on, nothing shows up in the terminal.
package lecture
import scala.io.{BufferedSource, Source}
object LectureQuestion {
def fileSum(fileName: String): Int = {
var arrayOfnumbers = Array[String]()
var fileOfnumbers: BufferedSource = Source.fromFile(fileName)
for (line <- fileOfnumbers.getLines()){
val splits: Array[String] =line.split("#")
for (number <- splits){
arrayOfnumbers :+ number
println(arrayOfnumbers.mkString(""))
}
//println(splits.mkString(" "))
}
3
}
def main(args: Array[String]): Unit = {
println(fileSum("data/fileOfnumbers.txt"))
}
}
I set up a blank array to append the numbers to. I tried switching var to val, but that wouldn't make sense as var is mutuable, meaning it can change. I'm pretty sure the way to add things to an array in Scala is :+, so I'm not sure what's going on.
In Scala all you would need is flatMap the List of a List and then sum the result.
Here your example simplified, as we have extracted the lines already:
import scala.util.Try
def listSum(lines: List[String]): Int = {
(for{
line <- lines
number <- line.split("#").map(n => Try(n.trim.toInt).getOrElse(0))
} yield number).sum
}
listSum(List("12#43#134#bad","13#54#47")) // -> 303
No vars, resp. no mutability needed. Just a nice for-comprehension;).
And for comparison the solution with flatMap:
def listSum(lines: List[String]): Int = {
lines
.flatMap(_.split("#").map(n => Try(n.trim.toInt).getOrElse(0)))
.sum
}

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))
}

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()

Swift array loop once, write many

Consider the following silly, simple example:
let arr = ["hey", "ho"]
let doubled = arr.map {$0 + $0}
let capitalized = arr.map {$0.capitalizedString}
As you can see, I'm processing the same initial array in multiple ways in order to end up with multiple processed arrays.
Now imagine that arr is very long and that I have many such processes generating many final arrays. I don't like the above code because we are looping multiple times, once for each map call. I'd prefer to loop just once.
Now, obviously we could handle this by brute force, i.e. by starting with multiple mutable arrays and writing into all of them on each iteration:
let arr = ["hey", "ho"]
var doubled = [String]()
var capitalized = [String]()
for s in arr {
doubled.append(s + s)
capitalized.append(s.capitalizedString)
}
Fine. But now we don't get the joy of using map. So my question is: is there a better, Swiftier way? In a hazy way I imagine myself using map, or something like map, to generate something like a tuple and magically splitting that tuple out into all resulting arrays as we iterate, as if I could say something like this (pseudocode, don't try this at home):
let arr = ["hey", "ho"]
let (doubled, capitalized) = arr.map { /* ???? */ }
If I were designing my own language, I might even permit a kind of splatting by assignment into a pseudo-array of lvalues:
let arr = ["hey", "ho"]
let [doubled, capitalized] = arr.map { /* ???? */ }
No big deal if it can't be done, but it would be fun to be able to talk this way.
How about a function, multimap, that takes a collection of transformations, and applies each one, returning them as an array of arrays:
// yay protocol extensions
extension SequenceType {
// looks like T->U works OK as a constraint
func multimap
<U, C: CollectionType
where C.Generator.Element == Generator.Element->U>
(transformations: C) -> [[U]] {
return transformations.map {
self.map($0)
}
}
}
Then use it like this:
let arr = ["hey", "ho"]
let double: String->String = { $0 + $0 }
let uppercase: String->String = { $0.uppercaseString }
arr.multimap([double, uppercase])
// returns [["heyhey", "hoho"], ["HEY", "HO"]]
Or it might be quite nice in variadic form:
extension SequenceType {
func multimap<U>(transformations: (Generator.Element->U)...) -> [[U]] {
return self.multimap(transformations)
}
}
arr.multimap({ $0 + $0 }, { $0.uppercaseString })
Edit: if you want separate variables, I think the best you can do is a destructure function (which you have to declare n times for each n-tuple unfortunately):
// I don't think this can't be expressed as a protocol extension quite yet
func destructure<C: CollectionType>(source: C) -> (C.Generator.Element,C.Generator.Element) {
precondition(source.count == 2)
return (source[source.startIndex],source[source.startIndex.successor()])
}
// and, since it's a function, let's declare pipe forward
// to make it easier to call
infix operator |> { }
func |> <T,U>(lhs: T, rhs: T->U) -> U {
return rhs(lhs)
}
And then you can declare the variables like this:
let (doubled,uppercased)
= arr.multimap({ $0 + $0 }, { $0.uppercaseString }) |> destructure
Yes this is a teensy bit inefficient because you have to build the array then rip it apart – but that’s really not going to be material, since the arrays are copy-on-write and we’re talking about a small number of them in the outer array.
edit: an excuse to use the new guard statement:
func destructure<C: Sliceable where C.SubSlice.Generator.Element == C.Generator.Element>(source: C) -> (C.Generator.Element,C.Generator.Element) {
guard let one = source.first else { fatalError("empty source") }
guard let two = dropFirst(source).first else { fatalError("insufficient elements") }
return (one,two)
}
What is wrong with your suggestion of tuple?
let arr = ["hey", "ho"]
let mapped = arr.map {e in
return (e + e, e.capitalizedString)
}
How about this, we process 'capitalized' array while we map the 'doubled' array:
let arr = ["hey", "ho"]
var capitalized = [String]()
let doubled = arr.map {(var myString) -> String in
capitalized.append(myString.capitalizedString)
return myString + myString
}
//doubled ["heyhey", "hoho"]
//capitalized: ["Hey", "Ho"]

Resources