Kotlin: How convert from Set to Map? - kotlin

I have Set<FlagFilter> and I need to convert it to Map<Class<out FlagFilter>, FlagFilter>.
I tried doing it like this:
val result: Map<Class<out FlagFilter>, FlagFilter> =
target
.takeIf { !it.isEmpty() }
?.map { mapOf(it.javaClass to it) }
?: emptyMap<>()
but instead of a Map it turns out to be a List and I get a compilation error:
Type mismatch.
Required: Map<Class<out FlagFilter>, FlagFilter>
Found: List<Map<Class<FlagFilter>, FlagFilter>>
What am I doing wrong? As if there is not enough operation, but I do not understand yet which one

map isn't anything to do with the Map type - it's a functional programming term (coming from a broader mathematical concept) that basically means a function that maps each input value to an output value.
So it's a transformation or conversion that takes a collection of items, transforms each one, and results in another collection with the same number of items. In Kotlin, you get a List of items (unless you're working with a Sequence, in which case you get another Sequence that yields the same number of items).
It's worth getting familiar with the kotlin.collections package - there's lots of useful stuff in there! But each function has a specific purpose, in terms of how they process the collection and what they return:
map - returns a new value for each item
onEach - returns the original items (allows you to do something with each, then continue processing the collection)
forEach - returns nothing (allows you to do something with each, but as a final operation - you can't chain another operation, it's terminal)
filter - returns a subset of the original items, matching a predicate
first - returns a single item, matching a predicate
reduce - returns a single item, transforming the values to produce a single result
count - returns a single item, based on an attribute of the collection (not the values themselves)
There's more but you get the idea - some things transform, some things pass-through, some things give you an identically sized collection, or a potentially smaller one, or a single value. map is just the one that takes a collection, and gives you a collection of the same size where every item has been (potentially) altered.
Like Vadik says, use one of the associate* functions to turn values into a Map object, effectively transforming each item in the collection into a Map.Entry. Which is technically mapping it to a mapping, so I wasn't totally accurate earlier when I said it's nothing to do with Maps, but I figured I'd save this thought til the end ;)

Just use associateBy extension function:
val result: Map<Class<out FlagFilter>, FlagFilter> =
target.associateBy { it.javaClass }
Or if you want to fix your code, remove the excessive call of mapOf(). Just convert your Set to List of Pairs, and then call toMap() to create a map:
val result: Map<Class<out FlagFilter>, FlagFilter> =
target.map { it.javaClass to it }.toMap()

Related

How to get last Key or Value in Kotlin Map collection

How can I get the last key or value in a Kotlin Map collection? It seems like it cannot be done by using an index value.
There's a couple ways it can be done. While you can't elegantly print a map directly, you may print it's entry set.
The first way, and the way that I DO NOT recommend, is by calling the .last() function on the entry set. This can be accomplished with testMap.entries.last(). The reason I don't recommend this method is because in real data this method is non-deterministic -- meaning there's no way to guarantee the characteristics of the value returned.
While I don't recommend this method, I don't know your application and this may be sufficient.
I DO recommend using the .sortedBy() function on your entry set, and then calling .last() on it. This allows you to make some sort of assumption about the results returned, something that is typically necessary, otherwise why do you want the last?
See this example comparing the two methods and then comparing the method against the order you would get if you iterate with the .forEach function:
fun main(args: Array<String>) {
val testMap = mutableMapOf<Long, String>()
testMap[1] = "Hello"
testMap[5] = "World"
testMap[3] = "Foobar"
println(testMap.entries.last())
println(testMap.entries.sortedBy { it.key }.last())
println("\norder via loop:")
testMap
.entries
.forEach {
println("\t$it")
}
}
Take a look at the output:
3=Foobar
5=World
order via loop:
1=Hello
5=World
3=Foobar
Here we see that the value returned from .last(), is the last value that was inserted into the map - the same happens with .forEach. This is okay, but usually we want our map to have some sort of order. In this example, i've called for the entry set to be sorted by the key value, so that our call to .last() on the entry set returns the key/value pair with the largest key.

how can i sort 2D mutableListof<Any> by the first element in Kotlin

how can i sort 2D mutable list of array by the first element of array?
val books = mutableListOf<Any>(
listof("abc","b",1),
listof("abb","y",2),
listof("abcl"."i",3)
)
i want to get sort this mutablelist by alphabetical order of the first element of each list.
output should be
[listof("abb","y",2), listof("abc","b",1), listof("abcl"."i",3) ]
You can do
books.sortBy { (it as List<*>).first() as String }
This is difficult, because you have very limited type information.
If you the elements of the inner lists always have three values of type String, String, Integer, you should probably use a triple:
val books = mutableListOf<Triple<String, String, Int>>(
Triple("abc","b",1),
Triple("abb","y",2),
Triple("abcl","i",3)
)
books.sortBy { t -> t.first }
If the inner lists are always lists, but with different lengths and types, but it is known that the are always strings you can do something like
val books = mutableListOf<List<Any>>(
listOf("abc","b",1),
listOf("abb","y",2),
listOf("abcl","i",3)
)
books.sortBy { t -> t.first() as String }
If you don't know any type information, and books is truly a MutableList<Any>, then you cannot compare: you don't what you are comparing.
You've got two problems here:
your list contains elements of Any, which doesn't imply any kind of "first element"
you can only compare things which implement Comparable (unless you pass in your own comparator, or do your own comparison logic in the sorting function)
First, if this list really is supposed to hold "lists of elements", then you should make that part of the type. We can use the general Collection type (or Iterable which it extends):
val books = mutableListOf<Collection<Any>>(
listof("abc","b",1),
...
Unfortunately that doesn't work for arrays, which are their own thing. If you want to be able to mix and match, you need to keep the MutableList<Any> type, and do some type checking in the sort function:
// all kinds of things going in here
val books = mutableListOf<Any>(
listOf("abc","b",1),
arrayOf("abb","y",2),
setOf("abcl","i",3)
)
books.sortedBy {
when(it) {
is Collection<*> -> it.first().toString()
is Array<*> -> it.first().toString()
// or your fallback could just be it.toString()
else -> throw Exception("can't compare this thing")
}
}
That example demonstrates your second problem too - how to sort a bunch of Anys. Since you said you want them alphabetically sorted (and without knowing what you're going to put in there besides strings and numbers) one approach is to just call toString() on everything.
That's one of the few methods Any has, so if you can't be more specific about the types in your "lists", you can at least sort on that. Whether it'll be any use out-of-the-box depends on the objects you put in there!

Convert LinkedHasSet from one type to another

I have a very simple problem, I need to convert a LinkedHashSet that holds one type of object, into another.
So basically what I want to do is something like this(if map could return anything else than TypeB:
LinkedHashSet<TypeA> firstSet
LinkedHashSet<TypeB> secondSet = firstSet.map {
TypeB(firstSet.value1, firstSet.value2)
}
This is mostly written to signalize what I want to achieve, of course it doesn't work. Could someone help me write this in Kotlin?
map returns a List, but you can use mapTo to insert the resulting elements directly into a collection that you provide as its first argument. This collection is also returned so you can assign it to secondSet:
val secondSet: LinkedHashSet<TypeB> = firstSet.mapTo(LinkedHashSet<TypeB>()) {
TypeB(it.value1, it.value2)
}
This is more efficient than using map because it avoids creating an intermediate List to hold the results.

How to create a new list of Strings from a list of Longs in Kotlin? (inline if possible)

I have a list of Longs in Kotlin and I want to make them strings for UI purposes with maybe some prefix or altered in some way. For example, adding "$" in the front or the word "dollars" at the end.
I know I can simply iterate over them all like:
val myNewStrings = ArrayList<String>()
longValues.forEach { myNewStrings.add("$it dollars") }
I guess I'm just getting nitpicky, but I feel like there is a way to inline this or change the original long list without creating a new string list?
EDIT/UPDATE: Sorry for the initial confusion of my terms. I meant writing the code in one line and not inlining a function. I knew it was possible, but couldn't remember kotlin's map function feature at the time of writing. Thank you all for the useful information though. I learned a lot, thanks.
You are looking for a map, a map takes a lambda, and creates a list based on the result of the lambda
val myNewStrings = longValues.map { "$it dollars" }
map is an extension that has 2 generic types, the first is for knowing what type is iterating and the second what type is going to return. The lambda we pass as argument is actually transform: (T) -> R so you can see it has to be a function that receives a T which is the source type and then returns an R which is the lambda result. Lambdas doesn't need to specify return because the last line is the return by default.
You can use the map-function on List. It creates a new list where every element has been applied a function.
Like this:
val myNewStrings = longValues.map { "$it dollars" }
In Kotlin inline is a keyword that refers to the compiler substituting a function call with the contents of the function directly. I don't think that's what you're asking about here. Maybe you meant you want to write the code on one line.
You might want to read over the Collections documentation, specifically the Mapping section.
The mapping transformation creates a collection from the results of a
function on the elements of another collection. The basic mapping
function is
map().
It applies the given lambda function to each subsequent element and
returns the list of the lambda results. The order of results is the
same as the original order of elements.
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
For your example, this would look as the others said:
val myNewStrings = longValues.map { "$it dollars" }
I feel like there is a way to inline this or change the original long list without creating a new string list?
No. You have Longs, and you want Strings. The only way is to create new Strings. You could avoid creating a new List by changing the type of the original list from List<Long> to List<Any> and editing it in place, but that would be overkill and make the code overly complex, harder to follow, and more error-prone.
Like people have said, unless there's a performance issue here (like a billion strings where you're only using a handful) just creating the list you want is probably the way to go. You have a few options though!
Sequences are lazily evaluated, when there's a long chain of operations they complete the chain on each item in turn, instead of creating an intermediate full list for every operation in the chain. So that can mean less memory use, and more efficiency if you only need certain items, or you want to stop early. They have overhead though so you need to be sure it's worth it, and for your use-case (turning a list into another list) there are no intermediate lists to avoid, and I'm guessing you're using the whole thing. Probably better to just make the String list, once, and then use it?
Your other option is to make a function that takes a Long and makes a String - whatever function you're passing to map, basically, except use it when you need it. If you have a very large number of Longs and you really don't want every possible String version in memory, just generate them whenever you display them. You could make it an extension function or property if you like, so you can just go
fun Long.display() = "$this dollars"
val Long.dollaridoos: String get() = "$this.dollars"
print(number.display())
print(number.dollaridoos)
or make a wrapper object holding your list and giving access to a stringified version of the values. Whatever's your jam
Also the map approach is more efficient than creating an ArrayList and adding to it, because it can allocate a list with the correct capacity from the get-go - arbitrarily adding to an unsized list will keep growing it when it gets too big, then it has to copy to another (larger) array... until that one fills up, then it happens again...

How to choose between asIterable() vs asSequence()? [duplicate]

Both of these interfaces define only one method
public operator fun iterator(): Iterator<T>
Documentation says Sequence is meant to be lazy. But isn't Iterable lazy too (unless backed by a Collection)?
The key difference lies in the semantics and the implementation of the stdlib extension functions for Iterable<T> and Sequence<T>.
For Sequence<T>, the extension functions perform lazily where possible, similarly to Java Streams intermediate operations. For example, Sequence<T>.map { ... } returns another Sequence<R> and does not actually process the items until a terminal operation like toList or fold is called.
Consider this code:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
It prints:
before sum 1 2
Sequence<T> is intended for lazy usage and efficient pipelining when you want to reduce the work done in terminal operations as much as possible, same to Java Streams. However, laziness introduces some overhead, which is undesirable for common simple transformations of smaller collections and makes them less performant.
In general, there is no good way to determine when it is needed, so in Kotlin stdlib laziness is made explicit and extracted to the Sequence<T> interface to avoid using it on all the Iterables by default.
For Iterable<T>, on contrary, the extension functions with intermediate operation semantics work eagerly, process the items right away and return another Iterable. For example, Iterable<T>.map { ... } returns a List<R> with the mapping results in it.
The equivalent code for Iterable:
val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()
This prints out:
1 2 before sum
As said above, Iterable<T> is non-lazy by default, and this solution shows itself well: in most cases it has good locality of reference thus taking advantage of CPU cache, prediction, prefetching etc. so that even multiple copying of a collection still works good enough and performs better in simple cases with small collections.
If you need more control over the evaluation pipeline, there is an explicit conversion to a lazy sequence with Iterable<T>.asSequence() function.
Completing hotkey's answer:
It is important to notice how Sequence and Iterable iterates throughout your elements:
Sequence example:
list.asSequence().filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
Log result:
filter - Map - Each; filter - Map - Each
Iterable example:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
filter - filter - Map - Map - Each - Each
Iterable is mapped to the java.lang.Iterable interface on the
JVM, and is implemented by commonly used collections, like List or
Set. The collection extension functions on these are evaluated
eagerly, which means they all immediately process all elements in
their input and return a new collection containing the result.
Here’s a simple example of using the collection functions to get the
names of the first five people in a list whose age is at least 21:
val people: List<Person> = getPeople()
val allowedEntrance = people
.filter { it.age >= 21 }
.map { it.name }
.take(5)
Target platform: JVMRunning on kotlin v. 1.3.61 First, the age check
is done for every single Person in the list, with the result put in a
brand new list. Then, the mapping to their names is done for every
Person who remained after the filter operator, ending up in yet
another new list (this is now a List<String>). Finally, there’s one
last new list created to contain the first five elements of the
previous list.
In contrast, Sequence is a new concept in Kotlin to represent a lazily
evaluated collection of values. The same collection extensions are
available for the Sequence interface, but these immediately return
Sequence instances that represent a processed state of the date, but
without actually processing any elements. To start processing, the
Sequence has to be terminated with a terminal operator, these are
basically a request to the Sequence to materialize the data it
represents in some concrete form. Examples include toList, toSet,
and sum, to mention just a few. When these are called, only the
minimum required number of elements will be processed to produce the
demanded result.
Transforming an existing collection to a Sequence is pretty
straightfoward, you just need to use the asSequence extension. As
mentioned above, you also need to add a terminal operator, otherwise
the Sequence will never do any processing (again, lazy!).
val people: List<Person> = getPeople()
val allowedEntrance = people.asSequence()
.filter { it.age >= 21 }
.map { it.name }
.take(5)
.toList()
Target platform: JVMRunning on kotlin v. 1.3.61 In this case, the
Person instances in the Sequence are each checked for their age, if
they pass, they have their name extracted, and then added to the
result list. This is repeated for each person in the original list
until there are five people found. At this point, the toList function
returns a list, and the rest of the people in the Sequence are not
processed.
There’s also something extra a Sequence is capable of: it can contain
an infinite number of items. With this in perspective, it makes sense
that operators work the way they do - an operator on an infinite
sequence could never return if it did its work eagerly.
As an example, here’s a sequence that will generate as many powers of
2 as required by its terminal operator (ignoring the fact that this
would quickly overflow):
generateSequence(1) { n -> n * 2 }
.take(20)
.forEach(::println)
You can find more here.
Iterable is good enough for most use cases, the way iteration is performed on them it works very well with caches because of the spatial locality. But the issue with them is that whole collection must pass through first intermediate operation before it moves to second and so on.
In sequence each item passes through the full pipeline before the next is handled.
This property can be determental to the performance of your code especially when iterating over large data set. so, if your terminal operation is very likely to terminate early then sequence should be preferred choice because you save by not performing unnecessary operations. for example
sequence.filter { getFilterPredicate() }
.map { getTransformation() }
.first { getSelector() }
In above case if first item satisfies the filter predicate and after map transformation meets the selection criteria then filter, map and first are invoked only once.
In case of iterable whole collection must first be filtered then mapped and then first selection starts