Store value with area keys instead unique key in kotlin? - arraylist

Accidentally discovered the magic ability in Kotlin to record value with keys area in kotlin
private val durationFromAngel:HashMap<ClosedFloatingPointRange<Float>, Long> =
hashMapOf(1F..4F to 15L, 5F..9F to 150L, 10F..14F to 300L, 15F..29F to 700L)
But haven't idea how work with this in case if i have uniqe input key
Next isn't correct.
val value = durationFromAngel[5F]
But i want get via this value 5F from range 1F..5F

A Hashmap won't really help you as you cannot look up by key. Instead use a List and find the range you are looking for:
val durationFromAngel = listOf(
1F..4F to 15L,
5F..9F to 150L,
10F..14F to 300L,
15F..29F to 700L
)
val (_, result) = durationFromAngel.find { (range) -> 6F in range }!!
println(result)
For sure you can also add an extension function as:
operator fun <N: Comparable<N>, T> List<Pair<ClosedFloatingPointRange<N>, T>>.get(value: N) =
this.find { (range) -> value in range }!!
val (range, value) = durationFromAngel[6f]
println(value)
but thats just syntactic sugar.
Try it!

Related

Kotlin Generic problem, UNCHECKED_CAST , required:Nothing

#file:Suppress("UNCHECKED_CAST")
data class Element<T>(
val key: String,
val valueOne: T,
val valueTwo: T,
val comparator: Comparator<T>,
val comparatorValue: CompareResult
)
enum class CompareResult(
val value: Int
) {
LESS(-1),
EQUAL(0),
GREATER_THAN(1)
}
fun <T> matchesComparison(list:Collection<Element<T>>): Pair<Boolean, List<String>> {
val failedComparisons = mutableListOf<String>()
for (element in list) {
val compareValue = element.comparator.compare(element.valueOne, element.valueTwo)
if (element.comparatorValue.value != compareValue) {
failedComparisons.add(element.key)
}
}
return Pair(failedComparisons.isEmpty(), failedComparisons)
}
val stringComparator = Comparator.comparing(String::toString)
val intComparator = Comparator.comparing(Int::toInt)
val elementsToCompare = listOf(
Element("number", 1, 2, intComparator, CompareResult.LESS),
Element("first name", "a", "a", stringComparator, CompareResult.EQUAL),
Element("last name", "a", "b", stringComparator, CompareResult.EQUAL)
)
matchesComparison(elementsToCompare).second.joinToString(", ","Failed elements: \"","\"")
I often get faced with comparing two different object properties with the same values.
As an example object A has props number,firstname,lastname. What i want to do is create a list have and have a function which goes over these Elements and returns which props have failed the comparison. I've managed to use generics for both the object and the matchesComparison function which returns the failed comparisons. The problem begins when i want to pass this list which is of type Collection<Element<out Any>> to this function is i get a type missmatch. instead of using unchecked casts to force the Comparator to be of type Any i would like to do this
val stringComparator = Comparator.comparing(String::toString)
val intComparator = Comparator.comparing(Int::toInt)
The result value that of the script above should be Failed elements: "last name"
I tried changing the signature of the function to out any but then the comparator.compare method has both params as of type Nothing. I really want to avoid unsing unchecked casts.
matchesComparison() doesn't need to be generic in this case. It doesn't really care what is the type of the whole input collection, so we can simply use * here.
Then we have another problem. The compiler isn't smart enough to notice that while we perform operations on a single element, all its properties are of matching types. As a result, it doesn't allow to use element.comparator on element.valueOne and element.valueTwo. To fix this problem, we simply need to create a separate function which works on a single Element, so it understand the type for all properties is the same:
fun matchesComparison(list:Collection<Element<*>>): Pair<Boolean, List<String>> {
fun <T> Element<T>.matches() = comparatorValue.value == comparator.compare(valueOne, valueTwo)
val failedComparisons = mutableListOf<String>()
for (element in list) {
if (!element.matches()) {
failedComparisons.add(element.key)
}
}
return Pair(failedComparisons.isEmpty(), failedComparisons)
}
Also, I believe such matches() function should be actually a member function of Element. It seems strange that while Element is pretty independent and it contains everything that is needed to perform a comparison, it still requires to use external code for this. If it would have a matches() function then we wouldn't need to care about its T. matches() would work with any Element.

How to add data to Map copying existing values based on List of identifiers

Sorry for the poor title but it is rather hard to describe my use case in a short sentence.
Context
I have the following model:
typealias Identifier = String
data class Data(val identifier: Identifier,
val data1: String,
val data2: String)
And I have three main data structures in my use case:
A Set of Identifiers that exist and are valid in a given context. Example:
val existentIdentifiers = setOf("A-1", "A-2", "B-1", "B-2", "C-1")
A Map that contains a List of Data objects per Identifier. Example:
val dataPerIdentifier: Map<Identifier, List<Data>> = mapOf(
"A-1" to listOf(Data("A-1", "Data-1-A", "Data-2-A"), Data("A-1", "Data-1-A", "Data-2-A")),
"B-1" to listOf(Data("B-1", "Data-1-B", "Data-2-B")),
"C-1" to listOf(Data("C-1", "Data-1-C", "Data-2-C"))
)
A List of Lists that group together the Identifiers that should share the same List<Data> (each List includes always 2 Identifiers). Example
val identifiersWithSameData = listOf(listOf("A-1", "A-2"), listOf("B-1", "B-2"))
Problem / Use Case
The problem that I am trying to tackle stems from the fact that dataPerIdentifier might not contain all identifiersWithSameData given that existentIdentifiers contains such missing Identifiers. I need to add those missing Identifier to dataPerIdentifier, copying the List<Data> already in there.
Example
Given the data in the Context section:
A-1=[Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A),
Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A)],
B-1=[Data(identifier=B-1, data1=Data-1-B, data2=Data-2-B)],
C-1=[Data(identifier=C-1, data1=Data-1-C, data2=Data-2-C)]
The desired outcome is to update dataPerIdentifier so that it includes:
A-1=[Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A),
Data(identifier=A-1, data1=Data-1-A, data2=Data-2-A)],
B-1=[Data(identifier=B-1, data1=Data-1-B, data2=Data-2-B)],
C-1=[Data(identifier=C-1, data1=Data-1-C, data2=Data-2-C)],
A-2=[Data(identifier=A-2, data1=Data-1-A, data2=Data-2-A),
Data(identifier=A-2, data1=Data-1-A, data2=Data-2-A)]
The reason is that existentIdentifiers contains A-2 that is missing in the initial dataPerIdentifier Map. B-2 is also missing in the initial dataPerIdentifier Map but existentIdentifiers does not contain it, so it is ignored.
Possible solution
I have already a working code (handleDataForMultipleIdentifiers() method is the one doing the heavy lifting), but it does not feel to be the cleanest or easiest to read:
fun main(args: Array<String>) {
val existentIdentifiers = setOf("A-1", "A-2", "B-1", "C-1")
val dataPerIdentifier: Map<Identifier, List<Data>> = mapOf(
"A-1" to listOf(Data("A-1", "Data-1-A", "Data-2-A"), Data("A-1", "Data-1-A", "Data-2-A")),
"B-1" to listOf(Data("B-1", "Data-1-B", "Data-2-B")),
"C-1" to listOf(Data("C-1", "Data-1-C", "Data-2-C"))
)
val identifiersWithSameData = listOf(listOf("A-1", "A-2"), listOf("B-1", "B-2"))
print("Original Data")
println(dataPerIdentifier)
print("Target Data")
println(dataPerIdentifier.handleDataForMultipleIdentifiers(identifiersWithSameData, existentIdentifiers))
}
fun Map<Identifier, List<Data>>.handleDataForMultipleIdentifiers(identifiersWithSameData: List<List<Identifier>>, existentIdentifiers: Set<Identifier>)
: Map<Identifier, List<Data>> {
val additionalDataPerIdentifier = identifiersWithSameData
.mapNotNull { identifiersList ->
val identifiersWithData = identifiersList.find { it in this.keys }
identifiersWithData?.let { it to identifiersList.minus(it).filter { it in existentIdentifiers } }
}.flatMap { (existentIdentifier, additionalIdentifiers) ->
val existentIdentifierData = this[existentIdentifier].orEmpty()
additionalIdentifiers.associateWith { identifier -> existentIdentifierData.map { it.copy(identifier = identifier) } }.entries
}.associate { it.key to it.value }
return this + additionalDataPerIdentifier
}
typealias Identifier = String
data class Data(val identifier: Identifier,
val data1: String,
val data2: String)
So my question is: how can I do this in a simpler way?
If identifiersWithSameData always contains 2 identifiers per item then it should not really be a list of lists, but rather a list of pairs or dedicated data classes. And if you convert this data structure into a map like this:
val identifiersWithSameData = mapOf("A-1" to "A-2", "A-2" to "A-1", "B-1" to "B-2", "B-2" to "B-1")
The the whole solution is pretty simple:
existentIdentifiers.associateWith {
dataPerIdentifier[it] ?: dataPerIdentifier[identifiersWithSameData[it]!!]!!
}
I'm not sure about both !!, for example I don't know if it is guaranteed that identifier existing in existentIdentifiers exists in identifiersWithSameData as well. You may need to tune this solution a little.

In Kotlin, how can I take the first n elements of an array

In Kotlin, how can I take the first n elements of this array:
val allColours = arrayOf(
Pair(Color.RED, Color.WHITE),
Pair(Color.RED, Color.BLACK),
Pair(Color.YELLOW, Color.BLACK),
Pair(Color.GREEN, Color.WHITE),
Pair(Color.BLUE, Color.WHITE),
Pair(Color.BLUE, Color.WHITE),
Pair(Color.CYAN, Color.BLACK),
Pair(Color.WHITE, Color.BLACK))
So how can I fill pegColours with the first say 3 Pairs?
var pegColours: Array<Pair<Color,Color>> = //???
I tried allColours.take but it gave an error:
Expecting an element
You need to specify the number of items you want to take.
allColours.take(3)
For a random number of random indices, you can use the following:
val indexes = arrayOf(2, 4, 6)
allColours.filterIndexed { index, s -> indexes.contains(index) }
Note that you can write an extension method for this:
fun <T> Array<T>.filterByIndices(vararg indices: Int) = filterIndexed { index, _ -> indices.contains(index) }
Alternatively, if the indices are consecutive, you can use slice:
allColours.slice(1..3)
The problem with your code that you create pairs with color constants which are Ints (allColours has type Array<Pair<Int, Int>>), but you expect Array<Pair<Color, Color>>. What you have to do is change type pegColours type and use take:
var pegColours: Array<Pair<Int, Int>> = allColours.take(3).toTypedArray()
Also you have to call toTypedArray() cause Array.take returns List rather than Array. Or you can change pegColours type as following:
var pegColours: List<Pair<Int, Int>> = allColours.take(3)
I know you already proposed the usage of take, but alternatively ranges and a simple map also help to write idiomatic code as shown next:
var pegColours = (0 until 3)
.map { allColours[it] }
.toTypedArray()
You are very close :)
val allColours = arrayOf("red", "blue", "green")
kotlin.io.println(allColours.take(2))
Will give you first two elements ["red", "blue"]
You have to specify the number of elements you want to take from the array

How do I create a map from 2 arrays?

I have a string array and an integer array. How do I create a map using the first as keys and the second as values?
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val map: Map<String, Int> = ???
How to convert List to Map in Kotlin? doesn't solve this problem; I have 2 arrays and want a single map.
You can zip together the arrays to get a list of pairs (List<Pair<String, Int>>), and then use toMap to get your map.
Like this:
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val map: Map<String, Int> =
keys.zip(values) // Gives you [("butter", 5), ("milk", 10), ("apples", 42)]
.toMap() // This is an extension function on Iterable<Pair<K, V>>
According to kotlin
Constructing Collections
-> creating a short-living Pair object, is not recommended only if performance isn't critical and to quote: "To avoid excessive memory usage, use alternative ways. For example, you can create a mutable map and populate it using the write operations. The apply() function can help to keep the initialization fluent here."
since I'm not much of an expert I run on to these code and maybe this should work better?:
val numbersMap = mutableMapOf<String,Int>()
.apply{ for (i in 1.. 5) this["key$i"] = i }
println(numbersMap)
//result = {key1=1, key2=2, key3=3, key4=4}
or to adjust it to question above - something like this:
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val mapNumber = mutableMapOf<String, Int>()
.apply { for (i in keys.indices) this[keys[i]] = values[i] }
println(mapNumber)

Why Can't I Say: val list = properties.map((key,value) -> "$key = $value")

In Kotlin I can say
//sweet
for ((key,value) in System.getProperties())
println("$key = $value")
but I cannot say
//sour
val properties = System.getProperties()
val list = properties.map((key,value) -> "$key = $value")
What is the Kotlin equivalent to properties.map{case (key, value) => s"$key = $value"} in Scala?
In Kotlin 1.0 you can say:
val properties = System.getProperties()
val list = properties.map { "${it.key} = ${it.value}" }
And if you prefer to unpack the map entries to separate values you can say:
val properties = System.getProperties()
val list = properties.map { val (key, value) = it; "$key = $value" }
In Kotlin 1.1 "you can now use the destructuring declaration syntax to unpack the arguments passed to a lambda" (What's New in Kotlin 1.1 - Kotlin Programming Language):
val properties = System.getProperties()
val list = properties.map { (key,value) -> "$key = $value" }
Your question has absolutely nothing with inference.
In scala you doc:
import collection.JavaConversions._
val properties = System.getProperties()
val list = properties.map{case (key,value) => s"$key = $value"}
As for your comments.
Having gone from years of Scala, to now exploring Kotlin, I tend to like Kotlin better in its power and simplicity, however, it would be nice to be able to infer things better, given that I am still in learning mode.
The issues in your code have nothing to do with type inference. Not even the syntax was right.
Please correct your syntax for lambda expressions and unpacking:
val properties = mapOf(1 to "First", 2 to "Second")
val list = properties.map {val (value, key) = it; "$key = $value"}
There is a Kotlin feature that is planned but will not be available in 1.0 which will allow writing {(key,value) -> "$key = $value"} to unpack a value type like a Pair – cypressious
Thanks #cypressious that was exactly what I was hoping for. Basically the answer is that Kotlin is still evolving and people are still getting around to polishing off cool stuff.
For now I have found that
//savory
val properties = System.getProperties()
val list = properties.map {property -> "${property.key} = ${property.value}"}
comes pretty close.